musora-content-services 2.107.2 → 2.107.4

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 (76) hide show
  1. package/.claude/settings.local.json +6 -9
  2. package/.yarnrc.yml +1 -0
  3. package/CHANGELOG.md +18 -0
  4. package/check_content.js +30 -0
  5. package/check_content.mjs +32 -0
  6. package/package.json +1 -1
  7. package/src/filterBuilder.js +0 -0
  8. package/src/index.d.ts +17 -0
  9. package/src/index.js +17 -0
  10. package/src/infrastructure/http/HttpClient.ts +0 -0
  11. package/src/lib/ads/monoid.ts +0 -0
  12. package/src/lib/ads/semigroup.ts +0 -0
  13. package/src/lib/sanity/filter.ts +0 -0
  14. package/src/lib/sanity/query.ts +0 -0
  15. package/src/services/awards/award-callbacks.js +0 -0
  16. package/src/services/awards/award-query.js +0 -0
  17. package/src/services/awards/internal/award-definitions.js +0 -0
  18. package/src/services/awards/internal/award-manager.js +0 -0
  19. package/src/services/awards/internal/certificate-builder.js +0 -0
  20. package/src/services/awards/types.d.ts +0 -0
  21. package/src/services/awards/types.js +0 -0
  22. package/src/services/content/artist.ts +5 -5
  23. package/src/services/content/genre.ts +5 -5
  24. package/src/services/content/instructor.ts +7 -6
  25. package/src/services/content-org/guided-courses.ts +0 -0
  26. package/src/services/content-org/learning-paths.ts +0 -0
  27. package/src/services/content-org/playlists.js +0 -0
  28. package/src/services/content.js +0 -0
  29. package/src/services/contentAggregator.js +0 -0
  30. package/src/services/contentProgress.js +0 -0
  31. package/src/services/forums/forums.ts +0 -0
  32. package/src/services/forums/posts.ts +50 -2
  33. package/src/services/forums/threads.ts +13 -0
  34. package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
  35. package/src/services/progress-events.js +0 -0
  36. package/src/services/progress-row/method-card.js +0 -0
  37. package/src/services/railcontent.js +80 -3
  38. package/src/services/recommendations.js +0 -0
  39. package/src/services/reporting/reporting.ts +71 -3
  40. package/src/services/sync/errors/index.ts +0 -0
  41. package/src/services/sync/errors/validators.ts +0 -0
  42. package/src/services/sync/fetch.ts +0 -0
  43. package/src/services/sync/manager.ts +0 -0
  44. package/src/services/sync/models/ContentLike.ts +0 -0
  45. package/src/services/sync/models/ContentProgress.ts +0 -0
  46. package/src/services/sync/models/Practice.ts +0 -0
  47. package/src/services/sync/models/PracticeDayNote.ts +0 -0
  48. package/src/services/sync/models/UserAwardProgress.ts +0 -0
  49. package/src/services/sync/repositories/content-progress.ts +0 -0
  50. package/src/services/sync/repositories/user-award-progress.ts +0 -0
  51. package/src/services/sync/retry.ts +0 -0
  52. package/src/services/sync/schema/index.ts +0 -0
  53. package/src/services/sync/store/index.ts +0 -0
  54. package/src/services/urlBuilder.ts +297 -0
  55. package/src/services/user/chat.js +0 -0
  56. package/src/services/user/interests.js +0 -0
  57. package/src/services/user/management.js +0 -0
  58. package/src/services/user/memberships.ts +0 -0
  59. package/src/services/user/notifications.js +0 -0
  60. package/src/services/user/onboarding.ts +12 -122
  61. package/src/services/user/profile.js +0 -0
  62. package/src/services/user/sessions.js +0 -0
  63. package/src/services/user/types.d.ts +0 -0
  64. package/src/services/user/types.js +0 -0
  65. package/src/services/userActivity.js +0 -0
  66. package/test/awards/award-completion-flow.test.js +0 -0
  67. package/test/initializeTests.js +0 -0
  68. package/test/learningPaths.test.js +0 -0
  69. package/test/lib/__snapshots__/filter.test.ts.snap +0 -0
  70. package/test/lib/filter.test.ts +0 -0
  71. package/test/lib/query.test.ts +0 -0
  72. package/test/reporting.test.js +132 -0
  73. package/test/sanityQueryService.test.js +0 -0
  74. package/test/sync/adapter.ts +0 -0
  75. package/test/sync/initialize-sync-manager.js +0 -0
  76. package/test_owned_navigate.js +74 -0
@@ -11,6 +11,10 @@ import { HttpClient } from '../../infrastructure/http/HttpClient'
11
11
  import { globalConfig } from '../config.js'
12
12
  import { ReportResponse, ReportableType, IssueTypeMap, ReportIssueOption } from './types'
13
13
  import { Brands } from '../../lib/brands'
14
+ import { generateContentUrl, generatePlaylistUrl, generateForumPostUrl, generateCommentUrl } from '../urlBuilder.ts'
15
+ import {fetchByRailContentId} from "../../index";
16
+ import {fetchByRailContentIds} from "../sanity";
17
+ import {addContextToContent} from "../contentAggregator";
14
18
 
15
19
  /**
16
20
  * Parameters for submitting a report with type-safe issue values
@@ -26,6 +30,14 @@ export type ReportParams<T extends ReportableType = ReportableType> = {
26
30
  details?: string
27
31
  /** Brand context (required: drumeo, pianote, guitareo, singeo, playbass) */
28
32
  brand: Brands | string
33
+ /** Full URL to the reported content (generated via urlBuilder) */
34
+ contentUrl?: string
35
+ /** Content data for URL generation (only needed if contentUrl not provided) */
36
+ content?: {
37
+ id: number
38
+ type: string
39
+ parentId?: number
40
+ }
29
41
  }
30
42
 
31
43
  /**
@@ -86,12 +98,68 @@ export async function report<T extends ReportableType>(
86
98
  requestBody.details = params.details
87
99
  }
88
100
 
89
- const response = await httpClient.post<ReportResponse>(
101
+ // Generate content_url for reports (relative URL - backend adds domain)
102
+ if (params.type === 'content') {
103
+ // Fetch content and add navigateTo for courses/packs/etc
104
+ const contents = await addContextToContent(
105
+ fetchByRailContentIds,
106
+ [params.id],
107
+ { addNavigateTo: true }
108
+ )
109
+ const content = contents?.[0]
110
+
111
+ if (content) {
112
+ requestBody.content_url = generateContentUrl({
113
+ id: content.id,
114
+ type: content.type,
115
+ parentId: content.parentId || content.parent_id,
116
+ brand: content.brand,
117
+ navigateTo: content.navigateTo
118
+ })
119
+ }
120
+ } else if (params.type === 'playlist') {
121
+ requestBody.content_url = generatePlaylistUrl({
122
+ id: params.id
123
+ })
124
+ } else if (params.type === 'forum_post') {
125
+ const { fetchPost } = await import('../forums/posts.ts')
126
+ const post = await fetchPost(params.id, params.brand)
127
+
128
+ if (post?.thread) {
129
+ requestBody.content_url = generateForumPostUrl({
130
+ brand: params.brand,
131
+ thread: {
132
+ category_id: post.thread.category_id,
133
+ id: post.thread.id
134
+ }
135
+ })
136
+ }
137
+ } else if (params.type === 'comment') {
138
+ const { fetchComment } = await import('../railcontent.js')
139
+ const comment = await fetchComment(params.id)
140
+
141
+ if (comment?.content) {
142
+ const contents = await fetchByRailContentIds([comment.content.id])
143
+ const content = contents?.[0]
144
+
145
+ if (content) {
146
+ requestBody.content_url = generateCommentUrl({
147
+ id: comment.id,
148
+ content: {
149
+ id: content.id,
150
+ type: content.type,
151
+ parentId: content.parentId || content.parent_id,
152
+ brand: params.brand
153
+ }
154
+ })
155
+ }
156
+ }
157
+ }
158
+
159
+ return await httpClient.post<ReportResponse>(
90
160
  '/api/user-reports/v1/reports',
91
161
  requestBody
92
162
  )
93
-
94
- return response
95
163
  }
96
164
 
97
165
  /**
File without changes
File without changes
File without changes
File without changes
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,297 @@
1
+ /**
2
+ * @module UrlBuilder
3
+ * @description URL generation for content across Musora platform
4
+ *
5
+ * This is the SINGLE SOURCE OF TRUTH for URL generation.
6
+ * Used by:
7
+ * - musora-platform-frontend (via import)
8
+ * - Mobile apps (via import)
9
+ * - Backend receives these URLs from frontend and stores them in DB
10
+ *
11
+ * Port of: musora-platform-frontend/src/shared/utils/content.utils.ts:generateContentUrl
12
+ * Related: musora-platform-backend/app/Modules/Content/Builders/UrlBuilder.php (deprecated fallback)
13
+ */
14
+
15
+ import { globalConfig } from './config.js'
16
+ import { Brands } from '../lib/brands.js'
17
+
18
+ /**
19
+ * Brand type - accepts enum values or string
20
+ */
21
+ export type Brand = Brands | string
22
+
23
+ /**
24
+ * Parameters for generating content URLs
25
+ */
26
+ export interface ContentUrlParams {
27
+ /** Content ID (required) */
28
+ id: number | string
29
+ /** Content type (required) */
30
+ type: string
31
+ /** Parent content ID (optional) */
32
+ parentId?: number
33
+ /** Navigation target (optional) */
34
+ navigateTo?: {
35
+ id: number
36
+ }
37
+ /** Brand (drumeo, pianote, guitareo, singeo, playbass) */
38
+ brand?: Brand
39
+ }
40
+
41
+ /**
42
+ * Forum post object for URL generation
43
+ */
44
+ export interface ForumPostUrlParams {
45
+ /** Brand (drumeo, pianote, etc) */
46
+ brand: Brand
47
+ /** Thread information */
48
+ thread: {
49
+ /** Thread category ID */
50
+ category_id: number
51
+ /** Thread ID */
52
+ id: number
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Playlist object for URL generation
58
+ */
59
+ export interface PlaylistUrlParams {
60
+ /** Playlist ID */
61
+ id: number
62
+ }
63
+
64
+ /**
65
+ * Comment object for URL generation
66
+ */
67
+ export interface CommentUrlParams {
68
+ /** Comment ID */
69
+ id: number
70
+ /** Content information */
71
+ content: {
72
+ /** Content ID */
73
+ id: number
74
+ /** Content type */
75
+ type: string
76
+ /** Parent content ID (optional) */
77
+ parentId?: number
78
+ /** Brand */
79
+ brand: Brand
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Generate a frontend URL for content
85
+ *
86
+ * @param params - Content parameters
87
+ * @returns The generated URL path
88
+ *
89
+ * @example
90
+ * generateContentUrl({ id: 123, type: 'song', brand: 'drumeo' })
91
+ * // Returns: "/drumeo/songs/transcription/123"
92
+ *
93
+ * @example
94
+ * generateContentUrl({ id: 456, type: 'course-part', parentId: 789, brand: 'pianote' })
95
+ * // Returns: "/pianote/lessons/course/789/456"
96
+ *
97
+ * @example
98
+ * generateContentUrl({ id: 123, type: 'pack-bundle', navigateTo: { id: 456 }, brand: 'guitareo' })
99
+ * // Returns: "/guitareo/lessons/pack/123/456"
100
+ */
101
+ export function generateContentUrl({
102
+ id,
103
+ type,
104
+ parentId,
105
+ navigateTo,
106
+ brand = 'drumeo',
107
+ }: ContentUrlParams): string {
108
+ // Special case: method homepage
109
+ if (type === 'method') {
110
+ return `/${brand}/method`
111
+ }
112
+
113
+ // Return fallback if required params missing
114
+ if (!id || !type) {
115
+ return '#'
116
+ }
117
+
118
+ // Special cases that don't follow the standard pattern
119
+ if (type === 'live') {
120
+ return `/${brand}/lessons/${id}/live`
121
+ }
122
+
123
+ if (type === 'pack') {
124
+ return `/${brand}/lessons/pack/overview/${id}`
125
+ }
126
+
127
+ if (type === 'pack-bundle') {
128
+ if (navigateTo?.id) {
129
+ return `/${brand}/lessons/pack/${id}/${navigateTo.id}`
130
+ }
131
+ // Fallback to overview if navigateTo is missing
132
+ return `/${brand}/lessons/pack/overview/${id}`
133
+ }
134
+
135
+ // Helper function to build URL with common parameters
136
+ const buildUrl = (typeSegments: string[]): string => {
137
+ const contentId = navigateTo ? `${id}/${navigateTo.id}` : id
138
+ const parentSegment = parentId ? `/${parentId}` : ''
139
+ return `/${brand}/${typeSegments.join('/')}${parentSegment}/${contentId}`
140
+ }
141
+
142
+ // Determine page type (songs, method, or lessons)
143
+ const songTypes = [
144
+ 'song',
145
+ 'song-tutorial',
146
+ 'song-tutorial-lesson',
147
+ 'transcription',
148
+ 'play-along',
149
+ 'jam-track',
150
+ ]
151
+
152
+ const methodTypes = ['learning-path-v2', 'learning-path-lesson-v2']
153
+
154
+ let pageType: string
155
+ if (songTypes.includes(type)) {
156
+ pageType = 'songs'
157
+ } else if (methodTypes.includes(type)) {
158
+ pageType = 'method'
159
+ } else {
160
+ pageType = 'lessons'
161
+ }
162
+
163
+ // Content type routing - maps specific types to URL segments
164
+ const contentTypeRoutes: Record<string, string> = {
165
+ // Lesson types
166
+ 'course-lesson': 'course',
167
+ 'guided-course-lesson': 'course',
168
+ 'guided-course': 'course',
169
+ 'pack-bundle-lesson': 'pack',
170
+ 'documentary-lesson': 'documentary',
171
+ 'skill-pack-lesson': 'skill-pack',
172
+
173
+ // Method types
174
+ 'learning-path-lesson-v2': 'lesson',
175
+ 'learning-path-v2': 'lesson',
176
+
177
+ // Song types
178
+ song: 'transcription',
179
+ 'song-tutorial': 'tutorial',
180
+ 'song-tutorial-lesson': 'tutorial',
181
+ }
182
+
183
+ // Use specific route if available, otherwise fall back to type as-is
184
+ const contentTypeSegment = contentTypeRoutes[type] || type
185
+
186
+ return buildUrl([pageType, contentTypeSegment])
187
+ }
188
+
189
+ /**
190
+ * Generate a full URL with domain
191
+ *
192
+ * @param params - Content parameters (same as generateContentUrl)
193
+ * @returns Full URL with domain from globalConfig.frontendUrl
194
+ *
195
+ * @example
196
+ * generateContentUrlWithDomain({ id: 123, type: 'song' })
197
+ * // Returns: "https://www.musora.com/drumeo/songs/transcription/123"
198
+ */
199
+ export function generateContentUrlWithDomain(params: ContentUrlParams): string {
200
+ const path = generateContentUrl(params)
201
+
202
+ if (path === '#') {
203
+ return '#'
204
+ }
205
+
206
+ return globalConfig.frontendUrl + path
207
+ }
208
+
209
+ /**
210
+ * Generate URL for a forum post
211
+ *
212
+ * @param post - Forum post object
213
+ * @param withDomain - Include domain from globalConfig.frontendUrl
214
+ * @returns Forum post URL
215
+ *
216
+ * @example
217
+ * generateForumPostUrl({ brand: 'drumeo', thread: { category_id: 12, id: 456 }})
218
+ * // Returns: "/drumeo/forums/threads/12/456"
219
+ */
220
+ export function generateForumPostUrl(
221
+ post: ForumPostUrlParams,
222
+ withDomain: boolean = false
223
+ ): string {
224
+ const path = `/${post.brand}/forums/threads/${post.thread.category_id}/${post.thread.id}`
225
+
226
+ if (withDomain) {
227
+ return globalConfig.frontendUrl + path
228
+ }
229
+
230
+ return path
231
+ }
232
+
233
+ /**
234
+ * Generate URL for a user playlist
235
+ *
236
+ * @param playlist - Playlist object
237
+ * @param withDomain - Include domain from globalConfig.frontendUrl
238
+ * @returns Playlist URL
239
+ *
240
+ * @example
241
+ * generatePlaylistUrl({ id: 123 })
242
+ * // Returns: "/playlists/123"
243
+ */
244
+ export function generatePlaylistUrl(
245
+ playlist: PlaylistUrlParams,
246
+ withDomain: boolean = false
247
+ ): string {
248
+ const path = `/playlists/${playlist.id}`
249
+
250
+ if (withDomain) {
251
+ return globalConfig.frontendUrl + path
252
+ }
253
+
254
+ return path
255
+ }
256
+
257
+ /**
258
+ * Generate URL for a comment (content URL with anchor)
259
+ *
260
+ * @param comment - Comment object
261
+ * @param withDomain - Include domain from globalConfig.frontendUrl
262
+ * @returns Comment URL with anchor
263
+ *
264
+ * @example
265
+ * generateCommentUrl({
266
+ * id: 789,
267
+ * content: { id: 123, type: 'song', brand: 'drumeo' }
268
+ * })
269
+ * // Returns: "/drumeo/songs/transcription/123#comment-789"
270
+ */
271
+ export function generateCommentUrl(
272
+ comment: CommentUrlParams,
273
+ withDomain: boolean = false
274
+ ): string {
275
+ if (!comment.content) {
276
+ return '#'
277
+ }
278
+
279
+ const contentUrl = generateContentUrl({
280
+ id: comment.content.id,
281
+ type: comment.content.type,
282
+ parentId: comment.content.parentId,
283
+ brand: comment.content.brand,
284
+ })
285
+
286
+ if (contentUrl === '#') {
287
+ return '#'
288
+ }
289
+
290
+ const path = `${contentUrl}#comment-${comment.id}`
291
+
292
+ if (withDomain) {
293
+ return globalConfig.frontendUrl + path
294
+ }
295
+
296
+ return path
297
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * @module Onboarding
3
3
  */
4
- import { HttpClient } from '../../infrastructure/http/HttpClient'
5
- import { Brands } from '../../lib/brands'
4
+ import { GET, POST, PUT } from '../../infrastructure/http/HttpClient'
6
5
  import { globalConfig } from '../config.js'
7
6
 
8
7
  export interface OnboardingSteps {
@@ -55,8 +54,7 @@ export async function startOnboarding({
55
54
  steps = {},
56
55
  marketingOptIn = false,
57
56
  }: StartOnboardingParams): Promise<Onboarding> {
58
- const httpClient = new HttpClient(globalConfig.baseUrl)
59
- return httpClient.post<Onboarding>(`/api/user-management-system/v1/onboardings`, {
57
+ return POST(`/api/user-management-system/v1/onboardings`, {
60
58
  email,
61
59
  brand,
62
60
  flow,
@@ -91,8 +89,7 @@ export async function updateOnboarding({
91
89
  is_completed = false,
92
90
  marketingOptIn = false,
93
91
  }: UpdateOnboardingParams): Promise<Onboarding> {
94
- const httpClient = new HttpClient(globalConfig.baseUrl)
95
- return httpClient.put<Onboarding>(`/api/user-management-system/v1/onboardings/${id}`, {
92
+ return PUT(`/api/user-management-system/v1/onboardings/${id}`, {
96
93
  email,
97
94
  brand,
98
95
  flow,
@@ -111,8 +108,7 @@ export async function updateOnboarding({
111
108
  * @throws {HttpError} - If the HTTP request fails.
112
109
  */
113
110
  export async function userOnboardingForBrand(brand: string): Promise<Onboarding> {
114
- const httpClient = new HttpClient(globalConfig.baseUrl)
115
- return httpClient.get<Onboarding>(
111
+ return GET(
116
112
  `/api/user-management-system/v1/users/${globalConfig.sessionConfig.userId}/onboardings/brand/${encodeURIComponent(brand)}`
117
113
  )
118
114
  }
@@ -132,126 +128,20 @@ export interface OnboardingRecommendedContent {
132
128
  }
133
129
  }
134
130
 
135
- const recommendedContentCache: { [brand: string]: OnboardingRecommendedContent } = {
136
- drumeo: {
137
- id: 415737,
138
- title: 'The Power Of Your Left Hand (Beginner)',
139
- difficulty: 'Beginner',
140
- lesson_count: 12,
141
- skill_count: 1,
142
- badge:
143
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
144
- description:
145
- 'Start your drumming journey with essential techniques and rhythms to get you playing quickly.',
146
- video: {
147
- external_id: '1002267396',
148
- hlsManifestUrl:
149
- 'https://player.vimeo.com/external/250467786.m3u8?s=52dc97fc96fe903d80bf71bc1b1709cc444db407&oauth2_token_id=1284792283',
150
- type: 'vimeo-video',
151
- },
152
- },
153
- pianote: {
154
- id: 412405,
155
- title: 'Getting Started On The Piano',
156
- difficulty: 'Beginner',
157
- lesson_count: 4,
158
- skill_count: 3,
159
- badge:
160
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
161
- description:
162
- 'The goal of this course is to introduce you to the keys, and get you playing a song as fast as possible. ',
163
- video: {
164
- external_id: '1001267395',
165
- hlsManifestUrl:
166
- 'https://player.vimeo.com/external/1001267395.m3u8?s=8f8d8a8a762f688058e6e6fd6704c402baf1b797&oauth2_token_id=1284792283',
167
- type: 'vimeo-video',
168
- },
169
- },
170
- guitareo: {
171
- id: 191346,
172
- title: 'Understanding Your Instrument',
173
- difficulty: 'Beginner',
174
- lesson_count: 6,
175
- skill_count: 5,
176
- badge:
177
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
178
- description:
179
- 'New to the acoustic guitar? Then this Course is for you! Learn everything you need to get started on the acoustic guitar, and start playing music as fast as possible!',
180
- video: {
181
- external_id: '1003267397',
182
- hlsManifestUrl:
183
- 'https://player.vimeo.com/external/166972298.m3u8?s=a93bfe96a4ce9ac5a4eba3441838847ef2eafc9b&oauth2_token_id=1284792283',
184
- type: 'vimeo-video',
185
- },
186
- },
187
- singeo: {
188
- id: 415737,
189
- title: 'Sound Like A Star — Mastering Iconic Pop Voices',
190
- difficulty: 'Beginner',
191
- lesson_count: 5,
192
- skill_count: 4,
193
- badge:
194
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
195
- description:
196
- 'Welcome to the Singing Starter Kit! This course will teach you everything you need to know to sound better when you sing! You will learn how your unique voice works so that you can develop vocal strength, accurate pitch, and find confidence singing your favorite songs. You can sing, and the Singing Starter Kit is the perfect way to start your singing journey.',
197
- video: {
198
- external_id: '1004267398',
199
- hlsManifestUrl:
200
- 'https://player.vimeo.com/external/1040159819.m3u8?s=f238ad1a650fb30a49c36d61996c982f06ffffb1&oauth2_token_id=1284792283',
201
- type: 'vimeo-video',
202
- },
203
- },
204
- playbass: {
205
- id: 191346,
206
- title: 'Understanding Your Instrument',
207
- difficulty: 'Beginner',
208
- lesson_count: 6,
209
- skill_count: 5,
210
- badge:
211
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
212
- description:
213
- 'New to the acoustic guitar? Then this Course is for you! Learn everything you need to get started on the acoustic guitar, and start playing music as fast as possible!',
214
- video: {
215
- external_id: '1003267397',
216
- hlsManifestUrl:
217
- 'https://player.vimeo.com/external/166972298.m3u8?s=a93bfe96a4ce9ac5a4eba3441838847ef2eafc9b&oauth2_token_id=1284792283',
218
- type: 'vimeo-video',
219
- },
220
- },
131
+ export interface OnboardingRecommendationResponse {
132
+ recommendation: OnboardingRecommendedContent
133
+ user_onboarding: Onboarding
221
134
  }
222
135
 
223
136
  /**
224
137
  * Fetches recommended content for onboarding based on the specified brand.
225
138
  *
226
- * @param {string} email - The user's email address.
227
- * @param {Brands} brand - The brand identifier.
228
- * @returns {Promise<OnboardingRecommendedContent>} - A promise that resolves with the recommended content.
139
+ * @param {number} onboardingId - The ID of the onboarding process.
140
+ * @returns {Promise<OnboardingRecommendationResponse>} - A promise that resolves with the recommended content.
229
141
  * @throws {HttpError} - If the HTTP request fails.
230
142
  */
231
143
  export async function getOnboardingRecommendedContent(
232
- email: string,
233
- brand: Brands
234
- ): Promise<OnboardingRecommendedContent> {
235
- // TODO: Replace with real API call when available
236
- if (recommendedContentCache[brand]) {
237
- return recommendedContentCache[brand]
238
- }
239
-
240
- return {
241
- id: 412405,
242
- title: 'Getting Started On The Piano',
243
- difficulty: 'Beginner',
244
- lesson_count: 4,
245
- skill_count: 3,
246
- badge:
247
- 'https://cdn.sanity.io/files/4032r8py/staging/9470587f03479b7c1f8019c3cbcbdfe12aa267f3.png',
248
- description:
249
- 'The goal of this course is to introduce you to the keys, and get you playing a song as fast as possible. ',
250
- video: {
251
- external_id: '1001267395',
252
- hlsManifestUrl:
253
- 'https://player.vimeo.com/external/1001267395.m3u8?s=8f8d8a8a762f688058e6e6fd6704c402baf1b797&oauth2_token_id=1284792283',
254
- type: 'vimeo-video',
255
- },
256
- }
144
+ onboardingId: number
145
+ ): Promise<OnboardingRecommendationResponse> {
146
+ return POST(`/api/user-management-system/v1/onboardings/${onboardingId}/recommendation`, {})
257
147
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes