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.
- package/.claude/settings.local.json +6 -9
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +18 -0
- package/check_content.js +30 -0
- package/check_content.mjs +32 -0
- package/package.json +1 -1
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +17 -0
- package/src/infrastructure/http/HttpClient.ts +0 -0
- package/src/lib/ads/monoid.ts +0 -0
- package/src/lib/ads/semigroup.ts +0 -0
- package/src/lib/sanity/filter.ts +0 -0
- package/src/lib/sanity/query.ts +0 -0
- package/src/services/awards/award-callbacks.js +0 -0
- package/src/services/awards/award-query.js +0 -0
- package/src/services/awards/internal/award-definitions.js +0 -0
- package/src/services/awards/internal/award-manager.js +0 -0
- package/src/services/awards/internal/certificate-builder.js +0 -0
- package/src/services/awards/types.d.ts +0 -0
- package/src/services/awards/types.js +0 -0
- package/src/services/content/artist.ts +5 -5
- package/src/services/content/genre.ts +5 -5
- package/src/services/content/instructor.ts +7 -6
- package/src/services/content-org/guided-courses.ts +0 -0
- package/src/services/content-org/learning-paths.ts +0 -0
- package/src/services/content-org/playlists.js +0 -0
- package/src/services/content.js +0 -0
- package/src/services/contentAggregator.js +0 -0
- package/src/services/contentProgress.js +0 -0
- package/src/services/forums/forums.ts +0 -0
- package/src/services/forums/posts.ts +50 -2
- package/src/services/forums/threads.ts +13 -0
- package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
- package/src/services/progress-events.js +0 -0
- package/src/services/progress-row/method-card.js +0 -0
- package/src/services/railcontent.js +80 -3
- package/src/services/recommendations.js +0 -0
- package/src/services/reporting/reporting.ts +71 -3
- package/src/services/sync/errors/index.ts +0 -0
- package/src/services/sync/errors/validators.ts +0 -0
- package/src/services/sync/fetch.ts +0 -0
- package/src/services/sync/manager.ts +0 -0
- package/src/services/sync/models/ContentLike.ts +0 -0
- package/src/services/sync/models/ContentProgress.ts +0 -0
- package/src/services/sync/models/Practice.ts +0 -0
- package/src/services/sync/models/PracticeDayNote.ts +0 -0
- package/src/services/sync/models/UserAwardProgress.ts +0 -0
- package/src/services/sync/repositories/content-progress.ts +0 -0
- package/src/services/sync/repositories/user-award-progress.ts +0 -0
- package/src/services/sync/retry.ts +0 -0
- package/src/services/sync/schema/index.ts +0 -0
- package/src/services/sync/store/index.ts +0 -0
- package/src/services/urlBuilder.ts +297 -0
- package/src/services/user/chat.js +0 -0
- package/src/services/user/interests.js +0 -0
- package/src/services/user/management.js +0 -0
- package/src/services/user/memberships.ts +0 -0
- package/src/services/user/notifications.js +0 -0
- package/src/services/user/onboarding.ts +12 -122
- package/src/services/user/profile.js +0 -0
- package/src/services/user/sessions.js +0 -0
- package/src/services/user/types.d.ts +0 -0
- package/src/services/user/types.js +0 -0
- package/src/services/userActivity.js +0 -0
- package/test/awards/award-completion-flow.test.js +0 -0
- package/test/initializeTests.js +0 -0
- package/test/learningPaths.test.js +0 -0
- package/test/lib/__snapshots__/filter.test.ts.snap +0 -0
- package/test/lib/filter.test.ts +0 -0
- package/test/lib/query.test.ts +0 -0
- package/test/reporting.test.js +132 -0
- package/test/sanityQueryService.test.js +0 -0
- package/test/sync/adapter.ts +0 -0
- package/test/sync/initialize-sync-manager.js +0 -0
- 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
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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 {
|
|
227
|
-
* @
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
package/test/initializeTests.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/lib/filter.test.ts
CHANGED
|
File without changes
|
package/test/lib/query.test.ts
CHANGED
|
File without changes
|