musora-content-services 2.7.2 → 2.8.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 (53) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/docs/ContentOrganization.html +2 -2
  3. package/docs/Gamification.html +2 -2
  4. package/docs/UserManagementSystem.html +2 -2
  5. package/docs/api_types.js.html +2 -2
  6. package/docs/config.js.html +2 -2
  7. package/docs/content-org_content-org.js.html +2 -2
  8. package/docs/content-org_playlists-types.js.html +2 -2
  9. package/docs/content-org_playlists.js.html +2 -4
  10. package/docs/content.js.html +2 -2
  11. package/docs/gamification_awards.js.html +2 -2
  12. package/docs/gamification_gamification.js.html +2 -2
  13. package/docs/gamification_types.js.html +2 -2
  14. package/docs/global.html +2 -2
  15. package/docs/index.html +2 -2
  16. package/docs/module-Awards.html +2 -2
  17. package/docs/module-Config.html +2 -2
  18. package/docs/module-Content-Services-V2.html +2 -2
  19. package/docs/module-Interests.html +2 -2
  20. package/docs/module-Permissions.html +2 -2
  21. package/docs/module-Playlists.html +13 -13
  22. package/docs/module-Railcontent-Services.html +2 -2
  23. package/docs/module-Sanity-Services.html +2 -2
  24. package/docs/module-Sessions.html +2 -2
  25. package/docs/module-User-Activity.html +7 -7
  26. package/docs/module-UserManagement.html +448 -23
  27. package/docs/module-UserProfile.html +2 -2
  28. package/docs/railcontent.js.html +2 -2
  29. package/docs/sanity.js.html +2 -2
  30. package/docs/userActivity.js.html +2 -12
  31. package/docs/user_interests.js.html +2 -2
  32. package/docs/user_management.js.html +85 -12
  33. package/docs/user_permissions.js.html +2 -2
  34. package/docs/user_profile.js.html +2 -2
  35. package/docs/user_sessions.js.html +2 -2
  36. package/docs/user_types.js.html +2 -2
  37. package/docs/user_user-management-system.js.html +2 -2
  38. package/link_mcs.sh +8 -6
  39. package/package.json +1 -1
  40. package/src/contentMetaData.js +2 -2
  41. package/src/contentTypeConfig.js +12 -7
  42. package/src/index.d.ts +9 -1
  43. package/src/index.js +9 -1
  44. package/src/lib/httpHelper.js +60 -20
  45. package/src/services/content.js +0 -0
  46. package/src/services/contentProgress.js +0 -0
  47. package/src/services/dateUtils.js +23 -0
  48. package/src/services/user/management.js +83 -10
  49. package/src/services/userActivity.js +183 -37
  50. package/test/mockData/mockData_progress_content.json +0 -0
  51. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  52. package/test/progressRows.test.js +0 -0
  53. package/test/userActivity.test.js +4 -4
@@ -139,8 +139,8 @@ export const singleLessonTypes = ['quick-tips', 'rudiment', 'coach-lessons'];
139
139
  export const practiceAlongsLessonTypes = ['workout', 'boot-camp','challenges'];
140
140
  export const performancesLessonTypes = ['performance','solo','drum-fest-international-2022'];
141
141
  export const documentariesLessonTypes = ['tama','sonor','history-of-electronic-drums','paiste-cymbals'];
142
- export const liveArchivesLessonTypes = ['podcast', 'coach-stream', 'live-streams'];
143
- export const studentArchivesLessonTypes = ['student-review', 'question-and-answer', 'student-focus','student-collaboration'];
142
+ export const liveArchivesLessonTypes = ['podcast', 'coach-stream', 'question-and-answer', 'live-streams'];
143
+ export const studentArchivesLessonTypes = ['student-review', 'student-focus','student-collaboration'];
144
144
  export const tutorialsLessonTypes = ['song-tutorial'];
145
145
  export const transcriptionsLessonTypes = ['song'];
146
146
  export const playAlongLessonTypes = ['play-along'];
@@ -181,19 +181,24 @@ export const lessonTypesMapping = {
181
181
  };
182
182
 
183
183
  export const progressTypesMapping = {
184
- 'lesson': [...singleLessonTypes,...practiceAlongsLessonTypes, ...liveArchivesLessonTypes, ...performancesLessonTypes, ...studentArchivesLessonTypes, ...documentariesLessonTypes],
184
+ 'lesson': [...singleLessonTypes,...practiceAlongsLessonTypes, ...liveArchivesLessonTypes, ...performancesLessonTypes, ...studentArchivesLessonTypes, ...documentariesLessonTypes, 'live'],
185
185
  'course': ['course'],
186
186
  'show': showsLessonTypes,
187
187
  'song tutorial': tutorialsLessonTypes,
188
- 'transcription': transcriptionsLessonTypes,
189
- 'tabs': transcriptionsLessonTypes,
190
- 'sheet music': transcriptionsLessonTypes,
188
+ 'songs': transcriptionsLessonTypes,
191
189
  'play-along': playAlongLessonTypes,
192
190
  'guided course': ['challenge'],
193
191
  'pack': ['pack', 'semester-pack'],
194
192
  'method': ['learning-path']
195
193
  };
196
194
 
195
+ export const songs = {
196
+ drumeo: 'transcription',
197
+ guitareo: 'tab',
198
+ pianote: 'sheet music',
199
+ singeo: 'sheet music',
200
+ }
201
+
197
202
  export const filterTypes = {
198
203
  lessons: [...individualLessonsTypes, ...collectionLessonTypes],
199
204
  songs: [...tutorialsLessonTypes, ...transcriptionsLessonTypes, ...playAlongLessonTypes, 'jam-track'],
@@ -207,7 +212,7 @@ export const recentTypes = {
207
212
 
208
213
  export let contentTypeConfig = {
209
214
  'progress-tracker': {
210
- fields: ['"parent_content_data": parent_content_data[].id','"lessons": child[]->{"id": railcontent_id, "slug":slug.current, "brand":brand, "type": _type, "lessons": child[]->{"id":railcontent_id, "slug":slug.current, "type": _type,"brand":brand}}'],
215
+ fields: ['"parent_content_data": parent_content_data[].id','"badge" : badge.asset->url','"lessons": child[]->{"id": railcontent_id, "slug":slug.current, "brand":brand, "type": _type, "lessons": child[]->{"id":railcontent_id, "slug":slug.current, "type": _type,"brand":brand}}'],
211
216
  },
212
217
  song: {
213
218
  fields: ['album', 'soundslice', 'instrumentless', `"resources": ${resourcesField}`],
package/src/index.d.ts CHANGED
@@ -68,6 +68,7 @@ import {
68
68
  import {
69
69
  convertToTimeZone,
70
70
  getMonday,
71
+ getTimeRemainingUntilLocal,
71
72
  getWeekNumber,
72
73
  isNextDay,
73
74
  isSameDate
@@ -221,7 +222,10 @@ import {
221
222
 
222
223
  import {
223
224
  blockUser,
224
- unblockUser
225
+ deletePicture,
226
+ unblockUser,
227
+ uploadPicture,
228
+ uploadPictureFromS3
225
229
  } from './services/user/management.js';
226
230
 
227
231
  import {
@@ -282,6 +286,7 @@ declare module 'musora-content-services' {
282
286
  createPracticeNotes,
283
287
  deleteComment,
284
288
  deleteItemsFromPlaylist,
289
+ deletePicture,
285
290
  deletePlaylist,
286
291
  deletePracticeSession,
287
292
  deleteUserActivity,
@@ -396,6 +401,7 @@ declare module 'musora-content-services' {
396
401
  getScheduleContentRows,
397
402
  getSortOrder,
398
403
  getTabResults,
404
+ getTimeRemainingUntilLocal,
399
405
  getUserMonthlyStats,
400
406
  getUserPractices,
401
407
  getUserWeeklyStats,
@@ -459,6 +465,8 @@ declare module 'musora-content-services' {
459
465
  updatePlaylist,
460
466
  updatePracticeNotes,
461
467
  updateUserPractice,
468
+ uploadPicture,
469
+ uploadPictureFromS3,
462
470
  verifyImageSRC,
463
471
  verifyLocalDataContext,
464
472
  }
package/src/index.js CHANGED
@@ -68,6 +68,7 @@ import {
68
68
  import {
69
69
  convertToTimeZone,
70
70
  getMonday,
71
+ getTimeRemainingUntilLocal,
71
72
  getWeekNumber,
72
73
  isNextDay,
73
74
  isSameDate
@@ -221,7 +222,10 @@ import {
221
222
 
222
223
  import {
223
224
  blockUser,
224
- unblockUser
225
+ deletePicture,
226
+ unblockUser,
227
+ uploadPicture,
228
+ uploadPictureFromS3
225
229
  } from './services/user/management.js';
226
230
 
227
231
  import {
@@ -281,6 +285,7 @@ export {
281
285
  createPracticeNotes,
282
286
  deleteComment,
283
287
  deleteItemsFromPlaylist,
288
+ deletePicture,
284
289
  deletePlaylist,
285
290
  deletePracticeSession,
286
291
  deleteUserActivity,
@@ -395,6 +400,7 @@ export {
395
400
  getScheduleContentRows,
396
401
  getSortOrder,
397
402
  getTabResults,
403
+ getTimeRemainingUntilLocal,
398
404
  getUserMonthlyStats,
399
405
  getUserPractices,
400
406
  getUserWeeklyStats,
@@ -458,6 +464,8 @@ export {
458
464
  updatePlaylist,
459
465
  updatePracticeNotes,
460
466
  updateUserPractice,
467
+ uploadPicture,
468
+ uploadPictureFromS3,
461
469
  verifyImageSRC,
462
470
  verifyLocalDataContext,
463
471
  };
@@ -1,44 +1,84 @@
1
1
  import { globalConfig } from '../services/config.js'
2
2
 
3
- export async function fetchJSONHandler(url, token, baseUrl, method = 'get', dataVersion = null, body = null) {
4
- let headers = {
3
+ export async function fetchJSONHandler(
4
+ url,
5
+ token,
6
+ baseUrl,
7
+ method = 'get',
8
+ dataVersion = null,
9
+ body = null
10
+ ) {
11
+ const headers = {
5
12
  'Content-Type': 'application/json',
6
13
  Accept: 'application/json',
7
14
  'X-CSRF-TOKEN': token,
8
15
  }
9
16
 
17
+ if (body) {
18
+ body = JSON.stringify(body)
19
+ }
20
+
21
+ try {
22
+ const response = await fetchHandler(url, token, baseUrl, method, headers, dataVersion, body)
23
+
24
+ if (response.ok) {
25
+ const contentType = response.headers.get('content-type')
26
+ if (
27
+ contentType &&
28
+ contentType.indexOf('application/json') !== -1 &&
29
+ response.status !== 204
30
+ ) {
31
+ return await response.json()
32
+ } else {
33
+ return await response.text()
34
+ }
35
+ } else {
36
+ console.error(`Fetch error: ${method} ${url} ${response.status} ${response.statusText}`)
37
+ console.log(response)
38
+ }
39
+ } catch (error) {
40
+ console.error('Fetch error:', error)
41
+ }
42
+ return null
43
+ }
44
+
45
+ export async function fetchHandler(
46
+ url,
47
+ token,
48
+ baseUrl,
49
+ method = 'get',
50
+ headers = {},
51
+ dataVersion = null,
52
+ body = null
53
+ ) {
54
+ let reqHeaders = {
55
+ ...headers,
56
+ Accept: 'application/json',
57
+ 'X-CSRF-TOKEN': token,
58
+ }
59
+
10
60
  if (!globalConfig.isMA) {
11
61
  const params = new URLSearchParams(window.location.search)
12
62
  if (params.get('testNow')) {
13
- headers['testNow'] = params.get('testNow')
63
+ reqHeaders['testNow'] = params.get('testNow')
14
64
  }
15
65
  if (params.get('timezone')) {
16
- headers['M-Client-Timezone'] = params.get('timezone')
66
+ reqHeaders['M-Client-Timezone'] = params.get('timezone')
17
67
  }
18
68
  }
19
69
 
20
- if (globalConfig.localTimezoneString) headers['M-Client-Timezone'] = globalConfig.localTimezoneString
21
- if (dataVersion) headers['Data-Version'] = dataVersion
70
+ if (globalConfig.localTimezoneString)
71
+ reqHeaders['M-Client-Timezone'] = globalConfig.localTimezoneString
72
+ if (dataVersion) reqHeaders['Data-Version'] = dataVersion
22
73
  const options = {
23
74
  method,
24
- headers,
75
+ headers: reqHeaders,
25
76
  }
26
- if (body) options.body = JSON.stringify(body)
77
+ if (body) options.body = body
27
78
  if (token) options.headers['Authorization'] = `Bearer ${token}`
28
79
  if (baseUrl && url.startsWith('/')) url = baseUrl + url
29
80
  try {
30
- const response = await fetch(url, options)
31
- if (response.ok) {
32
- const contentType = response.headers.get("content-type");
33
- if (contentType && contentType.indexOf("application/json") !== -1) {
34
- return await response.json()
35
- } else {
36
- return await response.text()
37
- }
38
- } else {
39
- console.error(`Fetch error: ${method} ${url} ${response.status} ${response.statusText}`)
40
- console.log(response)
41
- }
81
+ return fetch(url, options)
42
82
  } catch (error) {
43
83
  console.error('Fetch error:', error)
44
84
  }
File without changes
File without changes
@@ -53,3 +53,26 @@ export function isNextDay(prev, current) {
53
53
  );
54
54
  }
55
55
 
56
+ export function getTimeRemainingUntilLocal(targetUtcIsoString) {
57
+ const targetUTC = new Date(targetUtcIsoString);
58
+ if (isNaN(targetUTC.getTime())) {
59
+ return "00:00:00";
60
+ }
61
+
62
+ const now = new Date();
63
+ const diff = targetUTC.getTime() - now.getTime();
64
+
65
+ if (diff <= 0) {
66
+ return "00:00:00";
67
+ }
68
+
69
+ const totalSeconds = Math.floor(diff / 1000);
70
+ const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
71
+ const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
72
+ const seconds = String(totalSeconds % 60).padStart(2, '0');
73
+
74
+ return `${hours}:${minutes}:${seconds}`;
75
+ }
76
+
77
+
78
+
@@ -1,14 +1,9 @@
1
1
  /**
2
2
  * @module UserManagement
3
3
  */
4
- import { fetchHandler } from '../railcontent.js'
5
-
6
- /**
7
- * Exported functions that are excluded from index generation.
8
- *
9
- * @type {string[]}
10
- */
11
- const excludeFromGeneratedIndex = []
4
+ import { fetchHandler as railcontentFetchHandler } from '../railcontent.js'
5
+ import { fetchHandler, fetchJSONHandler } from '../../lib/httpHelper.js'
6
+ import { globalConfig } from '../config.js'
12
7
 
13
8
  const baseUrl = `/api/user-management-system`
14
9
 
@@ -19,7 +14,7 @@ const baseUrl = `/api/user-management-system`
19
14
  */
20
15
  export async function blockUser(userId) {
21
16
  const url = `${baseUrl}/v1/block/${userId}`
22
- return fetchHandler(url, 'post')
17
+ return railcontentFetchHandler(url, 'post')
23
18
  }
24
19
 
25
20
  /**
@@ -29,5 +24,83 @@ export async function blockUser(userId) {
29
24
  */
30
25
  export async function unblockUser(userId) {
31
26
  const url = `${baseUrl}/v1/unblock/${userId}`
32
- return fetchHandler(url, 'post')
27
+ return railcontentFetchHandler(url, 'post')
28
+ }
29
+
30
+ /**
31
+ * Upload a picture to the server
32
+ * @param {string} fieldKey
33
+ * @param {File} file
34
+ * @returns {Promise<any|string|null>}
35
+ */
36
+ export async function uploadPicture(fieldKey, file) {
37
+ const formData = new FormData()
38
+ formData.append('file', file)
39
+ formData.append('fieldKey', fieldKey)
40
+ const apiUrl = `${baseUrl}/v1/picture`
41
+
42
+ const response = await fetchHandler(
43
+ apiUrl,
44
+ globalConfig.sessionConfig.token,
45
+ globalConfig.baseUrl,
46
+ 'POST',
47
+ null,
48
+ null,
49
+ formData
50
+ )
51
+
52
+ if (!response.ok) {
53
+ const problemDetails = await response.json()
54
+ console.log('Error uploading picture:', problemDetails.detail)
55
+ throw new Error(`Upload failed: ${problemDetails.detail}`)
56
+ }
57
+
58
+ const { url } = await response.json()
59
+ console.log('Picture uploaded successfully:', url)
60
+
61
+ return url
62
+ }
63
+
64
+ /**
65
+ * Saves a picture uploaded to S3
66
+ * @param {string} fieldKey
67
+ * @param {string} s3_bucket_path
68
+ * @returns {Promise<any|string|null>}
69
+ */
70
+ export async function uploadPictureFromS3(fieldKey, s3_bucket_path) {
71
+ const apiUrl = `${baseUrl}/v1/picture/s3`
72
+
73
+ const response = await fetchJSONHandler(
74
+ apiUrl,
75
+ globalConfig.sessionConfig.token,
76
+ globalConfig.baseUrl,
77
+ 'POST',
78
+ null,
79
+ {
80
+ fieldKey,
81
+ s3_bucket_path,
82
+ }
83
+ )
84
+
85
+ if (!response.ok) {
86
+ const problemDetails = await response.json()
87
+ console.log('Error uploading picture:', problemDetails.detail)
88
+ throw new Error(`Upload failed: ${problemDetails.detail}`)
89
+ }
90
+
91
+ const { url } = await response.json()
92
+
93
+ return url
94
+ }
95
+
96
+ /**
97
+ * @param {string} pictureUrl
98
+ * @returns {Promise<any>}
99
+ */
100
+ export async function deletePicture(pictureUrl) {
101
+ const apiUrl = `${baseUrl}/v1/picture`
102
+
103
+ fetchJSONHandler(apiUrl, globalConfig.sessionConfig.token, globalConfig.baseUrl, 'DELETE', null, {
104
+ picture_url: pictureUrl,
105
+ })
33
106
  }