musora-content-services 2.104.5 → 2.104.7
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/src/lib/sanity/filter.ts +13 -0
- package/src/services/content/artist.ts +17 -25
- package/src/services/content/genre.ts +17 -25
- package/src/services/content/instructor.ts +15 -23
- package/src/services/content-org/learning-paths.ts +74 -36
- package/src/services/progress-row/method-card.js +38 -24
- package/src/services/sanity.js +8 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.104.7](https://github.com/railroadmedia/musora-content-services/compare/v2.104.6...v2.104.7) (2025-12-16)
|
|
6
|
+
|
|
7
|
+
### [2.104.6](https://github.com/railroadmedia/musora-content-services/compare/v2.104.5...v2.104.6) (2025-12-16)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* method card previous dailies ([#649](https://github.com/railroadmedia/musora-content-services/issues/649)) ([14f1c39](https://github.com/railroadmedia/musora-content-services/commit/14f1c390530a126d063eff3c7062e4fe2a09567c))
|
|
13
|
+
* **MU2-1315:** agi lessons permissions ([#657](https://github.com/railroadmedia/musora-content-services/issues/657)) ([b3ff00a](https://github.com/railroadmedia/musora-content-services/commit/b3ff00a2bb57e149981b18b5060ab2ec257db912))
|
|
14
|
+
|
|
5
15
|
### [2.104.5](https://github.com/railroadmedia/musora-content-services/compare/v2.104.4...v2.104.5) (2025-12-12)
|
|
6
16
|
|
|
7
17
|
|
package/package.json
CHANGED
package/src/lib/sanity/filter.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { filtersToGroq } from '../../contentTypeConfig'
|
|
2
2
|
import { getPermissionsAdapter } from '../../services/permissions/index'
|
|
3
3
|
import type { UserPermissions } from '../../services/permissions/PermissionsAdapter'
|
|
4
|
+
import { Brands } from '../brands'
|
|
4
5
|
import { filterOps } from './query'
|
|
5
6
|
|
|
6
7
|
// ============================================
|
|
@@ -454,6 +455,18 @@ export class Filters {
|
|
|
454
455
|
static progressIds(progressIds: number[]): string {
|
|
455
456
|
return progressIds.length > 0 ? Filters.idIn(progressIds) : Filters.empty
|
|
456
457
|
}
|
|
458
|
+
|
|
459
|
+
static async lessonCount(brand?: Brands | string): Promise<string> {
|
|
460
|
+
return Filters.count(
|
|
461
|
+
await Filters.combineAsync(
|
|
462
|
+
Filters.status(),
|
|
463
|
+
Filters.publishedDate(),
|
|
464
|
+
Filters.notDeprecated(),
|
|
465
|
+
Filters.referencesParent(),
|
|
466
|
+
brand ? Filters.brand(brand) : Filters.empty
|
|
467
|
+
)
|
|
468
|
+
)
|
|
469
|
+
}
|
|
457
470
|
}
|
|
458
471
|
|
|
459
472
|
// Default export
|
|
@@ -12,7 +12,7 @@ export interface Artist {
|
|
|
12
12
|
slug: string
|
|
13
13
|
name: string
|
|
14
14
|
thumbnail: string
|
|
15
|
-
|
|
15
|
+
lesson_count: number
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface Artists {
|
|
@@ -33,35 +33,27 @@ export interface Artists {
|
|
|
33
33
|
*/
|
|
34
34
|
export async function fetchArtists(
|
|
35
35
|
brand: Brands | string,
|
|
36
|
-
options: BuildQueryOptions
|
|
36
|
+
options: BuildQueryOptions
|
|
37
37
|
): Promise<Artists> {
|
|
38
|
-
const lessonFilter = f.combine(f.brand(brand), f.referencesParent())
|
|
39
38
|
const type = f.type('artist')
|
|
40
|
-
const
|
|
41
|
-
const
|
|
39
|
+
const postFilter = `lesson_count > 0`
|
|
40
|
+
const { sort = 'lower(name)', offset = 0, limit = 20 } = options
|
|
42
41
|
|
|
43
42
|
const data = query()
|
|
44
43
|
.and(type)
|
|
45
|
-
.order(
|
|
46
|
-
.slice(
|
|
44
|
+
.order(getSortOrder(sort, brand))
|
|
45
|
+
.slice(offset, limit)
|
|
47
46
|
.select(
|
|
48
47
|
'name',
|
|
49
48
|
`"slug": slug.current`,
|
|
50
49
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
51
|
-
`"
|
|
50
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
52
51
|
)
|
|
53
52
|
.postFilter(postFilter)
|
|
54
53
|
.build()
|
|
55
54
|
|
|
56
|
-
const total = query()
|
|
57
|
-
.and(type)
|
|
58
|
-
.select(`"lessonCount": ${lessonCount}`)
|
|
59
|
-
.postFilter(postFilter)
|
|
60
|
-
.build()
|
|
61
|
-
|
|
62
55
|
const q = `{
|
|
63
56
|
"data": ${data},
|
|
64
|
-
"total": count(${total})
|
|
65
57
|
}`
|
|
66
58
|
|
|
67
59
|
return fetchSanity(q, true, { processNeedAccess: false, processPageType: false })
|
|
@@ -83,8 +75,6 @@ export async function fetchArtistBySlug(
|
|
|
83
75
|
slug: string,
|
|
84
76
|
brand?: Brands | string
|
|
85
77
|
): Promise<Artist | null> {
|
|
86
|
-
const filter = f.combine(brand ? f.brand(brand) : f.empty, f.referencesParent())
|
|
87
|
-
|
|
88
78
|
const q = query()
|
|
89
79
|
.and(f.type('artist'))
|
|
90
80
|
.and(f.slug(slug))
|
|
@@ -92,7 +82,7 @@ export async function fetchArtistBySlug(
|
|
|
92
82
|
'name',
|
|
93
83
|
`"slug": slug.current`,
|
|
94
84
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
95
|
-
`"
|
|
85
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
96
86
|
)
|
|
97
87
|
.first()
|
|
98
88
|
.build()
|
|
@@ -146,15 +136,17 @@ export async function fetchArtistLessons(
|
|
|
146
136
|
sort = getSortOrder(sort, brand)
|
|
147
137
|
|
|
148
138
|
const restrictions = await f.combineAsync(
|
|
149
|
-
f.
|
|
150
|
-
f.
|
|
139
|
+
f.status(),
|
|
140
|
+
f.publishedDate(),
|
|
141
|
+
f.notDeprecated(),
|
|
142
|
+
f.referencesIDWithFilter(f.combine(f.type('artist'), f.slug(slug))),
|
|
143
|
+
f.brand(brand),
|
|
144
|
+
f.searchMatch('title', searchTerm),
|
|
145
|
+
f.includedFields(includedFields),
|
|
146
|
+
f.progressIds(progressIds)
|
|
151
147
|
)
|
|
152
148
|
|
|
153
149
|
const data = query()
|
|
154
|
-
.and(f.brand(brand))
|
|
155
|
-
.and(f.searchMatch('title', searchTerm))
|
|
156
|
-
.and(f.includedFields(includedFields))
|
|
157
|
-
.and(f.progressIds(progressIds))
|
|
158
150
|
.and(restrictions)
|
|
159
151
|
.order(sort)
|
|
160
152
|
.slice(offset, limit)
|
|
@@ -168,5 +160,5 @@ export async function fetchArtistLessons(
|
|
|
168
160
|
"total": count(${total})
|
|
169
161
|
}`
|
|
170
162
|
|
|
171
|
-
return fetchSanity(q, true, { processNeedAccess:
|
|
163
|
+
return fetchSanity(q, true, { processNeedAccess: true, processPageType: false })
|
|
172
164
|
}
|
|
@@ -11,8 +11,8 @@ import { Filters as f } from '../../lib/sanity/filter'
|
|
|
11
11
|
export interface Genre {
|
|
12
12
|
name: string
|
|
13
13
|
slug: string
|
|
14
|
-
lessons_count: number
|
|
15
14
|
thumbnail: string
|
|
15
|
+
lesson_count: number
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface Genres {
|
|
@@ -33,35 +33,27 @@ export interface Genres {
|
|
|
33
33
|
*/
|
|
34
34
|
export async function fetchGenres(
|
|
35
35
|
brand: Brands | string,
|
|
36
|
-
options: BuildQueryOptions
|
|
36
|
+
options: BuildQueryOptions
|
|
37
37
|
): Promise<Genres> {
|
|
38
|
-
const lesson = f.combine(f.brand(brand), f.referencesParent())
|
|
39
38
|
const type = f.type('genre')
|
|
40
|
-
const
|
|
41
|
-
const
|
|
39
|
+
const postFilter = `lesson_count > 0`
|
|
40
|
+
const { sort = 'lower(name)', offset = 0, limit = 20 } = options
|
|
42
41
|
|
|
43
42
|
const data = query()
|
|
44
43
|
.and(type)
|
|
45
|
-
.order(
|
|
46
|
-
.slice(
|
|
44
|
+
.order(getSortOrder(sort, brand))
|
|
45
|
+
.slice(offset, limit)
|
|
47
46
|
.select(
|
|
48
47
|
'name',
|
|
49
48
|
`"slug": slug.current`,
|
|
50
49
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
51
|
-
`"
|
|
50
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
52
51
|
)
|
|
53
52
|
.postFilter(postFilter)
|
|
54
53
|
.build()
|
|
55
54
|
|
|
56
|
-
const total = query()
|
|
57
|
-
.and(type)
|
|
58
|
-
.select(`"lessons_count": ${lessonCount}`)
|
|
59
|
-
.postFilter(postFilter)
|
|
60
|
-
.build()
|
|
61
|
-
|
|
62
55
|
const q = `{
|
|
63
56
|
"data": ${data},
|
|
64
|
-
"total": count(${total})
|
|
65
57
|
}`
|
|
66
58
|
|
|
67
59
|
return fetchSanity(q, true, { processNeedAccess: false, processPageType: false })
|
|
@@ -83,8 +75,6 @@ export async function fetchGenreBySlug(
|
|
|
83
75
|
slug: string,
|
|
84
76
|
brand?: Brands | string
|
|
85
77
|
): Promise<Genre | null> {
|
|
86
|
-
const filter = f.combine(brand ? f.brand(brand) : f.empty, f.referencesParent())
|
|
87
|
-
|
|
88
78
|
const q = query()
|
|
89
79
|
.and(f.type('genre'))
|
|
90
80
|
.and(f.slug(slug))
|
|
@@ -92,7 +82,7 @@ export async function fetchGenreBySlug(
|
|
|
92
82
|
'name',
|
|
93
83
|
`"slug": slug.current`,
|
|
94
84
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
95
|
-
`"
|
|
85
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
96
86
|
)
|
|
97
87
|
.first()
|
|
98
88
|
.build()
|
|
@@ -145,15 +135,17 @@ export async function fetchGenreLessons(
|
|
|
145
135
|
sort = getSortOrder(sort, brand)
|
|
146
136
|
|
|
147
137
|
const restrictions = await f.combineAsync(
|
|
148
|
-
f.
|
|
149
|
-
f.
|
|
138
|
+
f.status(),
|
|
139
|
+
f.publishedDate(),
|
|
140
|
+
f.notDeprecated(),
|
|
141
|
+
f.referencesIDWithFilter(f.combine(f.type('genre'), f.slug(slug))),
|
|
142
|
+
f.brand(brand),
|
|
143
|
+
f.searchMatch('title', searchTerm),
|
|
144
|
+
f.includedFields(includedFields),
|
|
145
|
+
f.progressIds(progressIds)
|
|
150
146
|
)
|
|
151
147
|
|
|
152
148
|
const data = query()
|
|
153
|
-
.and(f.brand(brand))
|
|
154
|
-
.and(f.searchMatch('title', searchTerm))
|
|
155
|
-
.and(f.includedFields(includedFields))
|
|
156
|
-
.and(f.progressIds(progressIds))
|
|
157
149
|
.and(restrictions)
|
|
158
150
|
.order(sort)
|
|
159
151
|
.slice(offset, limit)
|
|
@@ -167,5 +159,5 @@ export async function fetchGenreLessons(
|
|
|
167
159
|
"total": count(${total})
|
|
168
160
|
}`
|
|
169
161
|
|
|
170
|
-
return fetchSanity(q, true, { processNeedAccess:
|
|
162
|
+
return fetchSanity(q, true, { processNeedAccess: true, processPageType: false })
|
|
171
163
|
}
|
|
@@ -9,7 +9,7 @@ import { Brands } from '../../lib/brands'
|
|
|
9
9
|
import { Filters as f } from '../../lib/sanity/filter'
|
|
10
10
|
|
|
11
11
|
export interface Instructor {
|
|
12
|
-
|
|
12
|
+
lesson_count: number
|
|
13
13
|
slug: string
|
|
14
14
|
name: string
|
|
15
15
|
short_bio: string
|
|
@@ -37,32 +37,24 @@ export async function fetchInstructors(
|
|
|
37
37
|
options: BuildQueryOptions
|
|
38
38
|
): Promise<Instructors> {
|
|
39
39
|
const type = f.type('instructor')
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const postFilter = `lessonCount > 0`
|
|
40
|
+
const postFilter = `lesson_count > 0`
|
|
41
|
+
const { sort = 'lower(name)', offset = 0, limit = 20 } = options
|
|
43
42
|
|
|
44
43
|
const data = query()
|
|
45
44
|
.and(type)
|
|
46
|
-
.order(
|
|
47
|
-
.slice(
|
|
45
|
+
.order(getSortOrder(sort, brand))
|
|
46
|
+
.slice(offset, limit)
|
|
48
47
|
.select(
|
|
49
48
|
'name',
|
|
50
49
|
`"slug": slug.current`,
|
|
51
50
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
52
|
-
`"
|
|
51
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
53
52
|
)
|
|
54
53
|
.postFilter(postFilter)
|
|
55
54
|
.build()
|
|
56
55
|
|
|
57
|
-
const total = query()
|
|
58
|
-
.and(type)
|
|
59
|
-
.select(`"lessonCount": ${lessonCount}`)
|
|
60
|
-
.postFilter(postFilter)
|
|
61
|
-
.build()
|
|
62
|
-
|
|
63
56
|
const q = `{
|
|
64
57
|
"data": ${data},
|
|
65
|
-
"total": count(${total})
|
|
66
58
|
}`
|
|
67
59
|
|
|
68
60
|
return fetchSanity(q, true, { processNeedAccess: false, processPageType: false })
|
|
@@ -84,8 +76,6 @@ export async function fetchInstructorBySlug(
|
|
|
84
76
|
slug: string,
|
|
85
77
|
brand?: Brands | string
|
|
86
78
|
): Promise<Instructor | null> {
|
|
87
|
-
const filter = f.combine(brand ? f.brand(brand) : f.empty, f.referencesParent())
|
|
88
|
-
|
|
89
79
|
const q = query()
|
|
90
80
|
.and(f.type('instructor'))
|
|
91
81
|
.and(f.slug(slug))
|
|
@@ -94,7 +84,7 @@ export async function fetchInstructorBySlug(
|
|
|
94
84
|
`"slug": slug.current`,
|
|
95
85
|
'short_bio',
|
|
96
86
|
`"thumbnail": thumbnail_url.asset->url`,
|
|
97
|
-
`"
|
|
87
|
+
`"lesson_count": ${await f.lessonCount(brand)}`
|
|
98
88
|
)
|
|
99
89
|
.first()
|
|
100
90
|
.build()
|
|
@@ -144,14 +134,16 @@ export async function fetchInstructorLessons(
|
|
|
144
134
|
sort = getSortOrder(sort, brand)
|
|
145
135
|
|
|
146
136
|
const restrictions = await f.combineAsync(
|
|
147
|
-
f.
|
|
148
|
-
f.
|
|
137
|
+
f.status(),
|
|
138
|
+
f.publishedDate(),
|
|
139
|
+
f.notDeprecated(),
|
|
140
|
+
f.referencesIDWithFilter(f.combine(f.type('instructor'), f.slug(slug))),
|
|
141
|
+
f.brand(brand),
|
|
142
|
+
f.searchMatch('title', searchTerm),
|
|
143
|
+
f.includedFields(includedFields)
|
|
149
144
|
)
|
|
150
145
|
|
|
151
146
|
const data = query()
|
|
152
|
-
.and(f.brand(brand))
|
|
153
|
-
.and(f.searchMatch('title', searchTerm))
|
|
154
|
-
.and(f.includedFields(includedFields))
|
|
155
147
|
.and(restrictions)
|
|
156
148
|
.order(sort)
|
|
157
149
|
.slice(offset, limit)
|
|
@@ -165,5 +157,5 @@ export async function fetchInstructorLessons(
|
|
|
165
157
|
"total": count(${total})
|
|
166
158
|
}`
|
|
167
159
|
|
|
168
|
-
return fetchSanity(q, true, { processNeedAccess:
|
|
160
|
+
return fetchSanity(q, true, { processNeedAccess: true, processPageType: false })
|
|
169
161
|
}
|
|
@@ -72,7 +72,7 @@ export async function getDailySession(brand: string, userDate: Date) {
|
|
|
72
72
|
export async function updateDailySession(
|
|
73
73
|
brand: string,
|
|
74
74
|
userDate: Date,
|
|
75
|
-
keepFirstLearningPath: boolean
|
|
75
|
+
keepFirstLearningPath: boolean = false
|
|
76
76
|
) {
|
|
77
77
|
const stringDate = userDate.toISOString().split('T')[0]
|
|
78
78
|
const url: string = `${LEARNING_PATHS_PATH}/daily-session/create`
|
|
@@ -194,6 +194,23 @@ export function mapContentToParent(lessons, parentContentType, parentContentId)
|
|
|
194
194
|
})
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
interface fetchLearningPathLessonsResponse {
|
|
198
|
+
id: number
|
|
199
|
+
thumbnail?: string
|
|
200
|
+
title: string
|
|
201
|
+
children: any[]
|
|
202
|
+
is_active_learning_path: boolean
|
|
203
|
+
active_learning_path_id?: number
|
|
204
|
+
active_learning_path_created_at?: string
|
|
205
|
+
upcoming_lessons?: any[]
|
|
206
|
+
completed_lessons?: any[]
|
|
207
|
+
learning_path_dailies?: any[]
|
|
208
|
+
next_learning_path_dailies?: any[]
|
|
209
|
+
next_learning_path_id?: number
|
|
210
|
+
previous_learning_path_dailies?: any[]
|
|
211
|
+
previous_learning_path_id?: number
|
|
212
|
+
}
|
|
213
|
+
|
|
197
214
|
/** Fetches and organizes learning path lessons.
|
|
198
215
|
*
|
|
199
216
|
* @param {number} learningPathId - The learning path ID.
|
|
@@ -203,13 +220,17 @@ export function mapContentToParent(lessons, parentContentType, parentContentId)
|
|
|
203
220
|
* @returns {number} result.id - The learning path ID.
|
|
204
221
|
* @returns {string} result.thumbnail - Optional thumbnail URL for the learning path.
|
|
205
222
|
* @returns {string} result.title - The title of the learning path.
|
|
206
|
-
* @returns {boolean} result.is_active_learning_path - Whether the learning path is currently active.
|
|
207
223
|
* @returns {Array} result.children - Array of all lessons.
|
|
208
|
-
* @returns {
|
|
209
|
-
* @returns {
|
|
210
|
-
* @returns {
|
|
211
|
-
* @returns {Array} result.
|
|
212
|
-
* @returns {Array} result.
|
|
224
|
+
* @returns {boolean} result.is_active_learning_path - Whether the learning path is currently active.
|
|
225
|
+
* @returns {number} result.active_learning_path_id - The active learning path ID from daily session.
|
|
226
|
+
* @returns {string} result.active_learning_path_created_at - The datetime the learning path was set as active.
|
|
227
|
+
* @returns {Array} result.upcoming_lessons - Array of upcoming lessons.
|
|
228
|
+
* @returns {Array} result.learning_path_dailies - Array of today's dailies in this learning path.
|
|
229
|
+
* @returns {Array} result.next_learning_path_dailies - Array of today's dailies in the next learning path.
|
|
230
|
+
* @returns {number} result.next_learning_path_id - the next learning path (after the active path).
|
|
231
|
+
* @returns {Array} result.completed_lessons - Array of completed lessons in this learning path.
|
|
232
|
+
* @returns {Array} result.previous_learning_path_dailies - Array of today's dailies in the previous learning path.
|
|
233
|
+
* @returns {number} result.previous_learning_path_id - the previous learning path (before the active path)
|
|
213
234
|
*/
|
|
214
235
|
export async function fetchLearningPathLessons(
|
|
215
236
|
learningPathId: number,
|
|
@@ -217,9 +238,9 @@ export async function fetchLearningPathLessons(
|
|
|
217
238
|
userDate: Date
|
|
218
239
|
) {
|
|
219
240
|
const learningPath = await getEnrichedLearningPath(learningPathId)
|
|
220
|
-
let dailySession = await getDailySession(brand, userDate)
|
|
241
|
+
let dailySession = await getDailySession(brand, userDate) as DailySessionResponse // what if the call just fails, and a DS does exist?
|
|
221
242
|
if (!dailySession) {
|
|
222
|
-
dailySession = await updateDailySession(brand, userDate, false)
|
|
243
|
+
dailySession = await updateDailySession(brand, userDate, false) as DailySessionResponse
|
|
223
244
|
}
|
|
224
245
|
|
|
225
246
|
const isActiveLearningPath = (dailySession?.active_learning_path_id || 0) == learningPathId
|
|
@@ -227,48 +248,64 @@ export async function fetchLearningPathLessons(
|
|
|
227
248
|
return {
|
|
228
249
|
...learningPath,
|
|
229
250
|
is_active_learning_path: isActiveLearningPath,
|
|
230
|
-
}
|
|
251
|
+
} as fetchLearningPathLessonsResponse
|
|
231
252
|
}
|
|
232
|
-
// this assumes that the first entry is active_path, based on user flows
|
|
233
|
-
const todayContentIds = dailySession.daily_session[0]?.content_ids || []
|
|
234
|
-
const todayLearningPathId = dailySession.daily_session[0]?.learning_path_id
|
|
235
253
|
|
|
236
|
-
|
|
237
|
-
|
|
254
|
+
let todayContentIds = []
|
|
255
|
+
let todayLearningPathId = null
|
|
256
|
+
let nextContentIds = []
|
|
257
|
+
let nextLearningPathId = null
|
|
258
|
+
let previousContentIds = []
|
|
259
|
+
let previousLearningPathId = null
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
for (const session of dailySession.daily_session) {
|
|
264
|
+
if (session.learning_path_id === learningPathId) {
|
|
265
|
+
todayContentIds = session.content_ids || []
|
|
266
|
+
todayLearningPathId = session.learning_path_id
|
|
267
|
+
} else {
|
|
268
|
+
if (!todayLearningPathId) {
|
|
269
|
+
previousContentIds = session.content_ids || []
|
|
270
|
+
previousLearningPathId = session.learning_path_id
|
|
271
|
+
} else if (!nextLearningPathId) {
|
|
272
|
+
nextContentIds = session.content_ids || []
|
|
273
|
+
nextLearningPathId = session.learning_path_id
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
238
277
|
|
|
239
278
|
const completedLessons = []
|
|
240
279
|
let thisLPDailies = []
|
|
241
280
|
let nextLPDailies = []
|
|
242
|
-
let
|
|
281
|
+
let previousLPDailies = []
|
|
243
282
|
const upcomingLessons = []
|
|
244
283
|
|
|
284
|
+
//previous/next never within LP
|
|
245
285
|
learningPath.children.forEach((lesson: any) => {
|
|
246
286
|
if (todayContentIds.includes(lesson.id)) {
|
|
247
287
|
thisLPDailies.push(lesson)
|
|
248
|
-
} else if (lesson.progressStatus ===
|
|
288
|
+
} else if (lesson.progressStatus === STATE.COMPLETED) {
|
|
249
289
|
completedLessons.push(lesson)
|
|
250
290
|
} else {
|
|
251
291
|
upcomingLessons.push(lesson)
|
|
252
292
|
}
|
|
253
293
|
})
|
|
254
294
|
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
todayContentIds,
|
|
260
|
-
todayLearningPathId
|
|
295
|
+
if (previousContentIds.length !== 0) {
|
|
296
|
+
previousLPDailies = await getLearningPathLessonsByIds(
|
|
297
|
+
previousContentIds,
|
|
298
|
+
previousLearningPathId
|
|
261
299
|
)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
nextLPDailies = lessons.map(lesson => ({
|
|
300
|
+
}
|
|
301
|
+
if (nextContentIds.length !== 0) {
|
|
302
|
+
nextLPDailies = await getLearningPathLessonsByIds(
|
|
303
|
+
nextContentIds,
|
|
304
|
+
nextLearningPathId
|
|
305
|
+
).then(lessons => lessons.map(lesson => ({
|
|
269
306
|
...lesson,
|
|
270
|
-
in_next_learning_path:
|
|
271
|
-
}))
|
|
307
|
+
in_next_learning_path: learningPath.progressStatus === STATE.COMPLETED
|
|
308
|
+
})))
|
|
272
309
|
}
|
|
273
310
|
|
|
274
311
|
return {
|
|
@@ -277,11 +314,12 @@ export async function fetchLearningPathLessons(
|
|
|
277
314
|
active_learning_path_id: dailySession?.active_learning_path_id,
|
|
278
315
|
active_learning_path_created_at: dailySession?.active_learning_path_created_at,
|
|
279
316
|
upcoming_lessons: upcomingLessons,
|
|
280
|
-
todays_lessons: thisLPDailies,
|
|
281
|
-
next_learning_path_lessons: nextLPDailies,
|
|
282
|
-
next_learning_path_id: nextLearningPathId,
|
|
283
317
|
completed_lessons: completedLessons,
|
|
284
|
-
|
|
318
|
+
learning_path_dailies: thisLPDailies,
|
|
319
|
+
next_learning_path_dailies: nextLPDailies,
|
|
320
|
+
next_learning_path_id: nextLearningPathId,
|
|
321
|
+
previous_learning_path_dailies: previousLPDailies,
|
|
322
|
+
previous_learning_path_id: previousLearningPathId,
|
|
285
323
|
}
|
|
286
324
|
}
|
|
287
325
|
|
|
@@ -6,7 +6,7 @@ import { getActivePath, fetchLearningPathLessons } from '../content-org/learning
|
|
|
6
6
|
import { getToday } from '../dateUtils.js'
|
|
7
7
|
import { fetchMethodV2IntroVideo } from '../sanity'
|
|
8
8
|
import { getProgressState } from '../contentProgress'
|
|
9
|
-
import {
|
|
9
|
+
import {COLLECTION_TYPE, STATE} from '../sync/models/ContentProgress'
|
|
10
10
|
|
|
11
11
|
export async function getMethodCard(brand) {
|
|
12
12
|
const introVideo = await fetchMethodV2IntroVideo(brand)
|
|
@@ -19,7 +19,7 @@ export async function getMethodCard(brand) {
|
|
|
19
19
|
|
|
20
20
|
const activeLearningPath = await getActivePath(brand)
|
|
21
21
|
|
|
22
|
-
if (introVideoProgressState !==
|
|
22
|
+
if (introVideoProgressState !== STATE.COMPLETED || !activeLearningPath) {
|
|
23
23
|
//startLearningPath('drumeo', 422533)
|
|
24
24
|
const timestamp = Math.floor(Date.now() / 1000)
|
|
25
25
|
const instructorText =
|
|
@@ -43,43 +43,57 @@ export async function getMethodCard(brand) {
|
|
|
43
43
|
progressTimestamp: timestamp,
|
|
44
44
|
}
|
|
45
45
|
} else {
|
|
46
|
-
//TODO: Optimize loading of dailySessions/Path, should not need multiple requests
|
|
47
46
|
const learningPath = await fetchLearningPathLessons(
|
|
48
47
|
activeLearningPath.active_learning_path_id,
|
|
49
48
|
brand,
|
|
50
49
|
getToday()
|
|
51
50
|
)
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const anyCompleted = learningPath?.todays_lessons.some(
|
|
58
|
-
(lesson) => lesson.progressStatus === 'completed'
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
const noneCompleted = learningPath?.todays_lessons.every(
|
|
62
|
-
(lesson) => lesson.progressStatus !== 'completed'
|
|
63
|
-
)
|
|
52
|
+
if (!learningPath) {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
// need to calculate based on all dailies
|
|
57
|
+
const allDailies = [
|
|
58
|
+
...learningPath.previous_learning_path_dailies,
|
|
59
|
+
...learningPath.learning_path_dailies,
|
|
60
|
+
...learningPath.next_learning_path_dailies
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
let allDailiesCompleted = true;
|
|
64
|
+
let anyDailiesCompleted = false;
|
|
65
|
+
let noDailiesCompleted = true;
|
|
66
|
+
let nextIncompleteDaily = null;
|
|
67
|
+
|
|
68
|
+
for (const lesson of allDailies) {
|
|
69
|
+
if (lesson.progressStatus === STATE.COMPLETED) {
|
|
70
|
+
anyDailiesCompleted = true;
|
|
71
|
+
noDailiesCompleted = false;
|
|
72
|
+
} else {
|
|
73
|
+
allDailiesCompleted = false;
|
|
74
|
+
if (!nextIncompleteDaily) {
|
|
75
|
+
nextIncompleteDaily = lesson;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!allDailiesCompleted && anyDailiesCompleted && nextIncompleteDaily) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
68
82
|
|
|
69
83
|
// get the first incomplete lesson from upcoming and next learning path lessons
|
|
70
84
|
const nextLesson = [
|
|
71
85
|
...learningPath?.upcoming_lessons,
|
|
72
|
-
...learningPath?.
|
|
73
|
-
]?.find((lesson) => lesson.progressStatus !==
|
|
86
|
+
...learningPath?.next_learning_path_dailies,
|
|
87
|
+
]?.find((lesson) => lesson.progressStatus !== STATE.COMPLETED)
|
|
74
88
|
|
|
75
89
|
let ctaText, action
|
|
76
|
-
if (
|
|
90
|
+
if (noDailiesCompleted) {
|
|
77
91
|
ctaText = 'Start Session'
|
|
78
|
-
action = getMethodActionCTA(
|
|
79
|
-
} else if (
|
|
92
|
+
action = getMethodActionCTA(nextIncompleteDaily)
|
|
93
|
+
} else if (anyDailiesCompleted && !allDailiesCompleted) {
|
|
80
94
|
ctaText = 'Continue Session'
|
|
81
|
-
action = getMethodActionCTA(
|
|
82
|
-
} else if (
|
|
95
|
+
action = getMethodActionCTA(nextIncompleteDaily)
|
|
96
|
+
} else if (allDailiesCompleted) {
|
|
83
97
|
ctaText = nextLesson ? 'Start Next Lesson' : 'Browse Lessons'
|
|
84
98
|
action = nextLesson
|
|
85
99
|
? getMethodActionCTA(nextLesson)
|
package/src/services/sanity.js
CHANGED
|
@@ -1306,7 +1306,7 @@ export async function fetchByReference(
|
|
|
1306
1306
|
* @returns {Promise<int|null>}
|
|
1307
1307
|
*/
|
|
1308
1308
|
export async function fetchTopLevelParentId(railcontentId) {
|
|
1309
|
-
const parentFilter =
|
|
1309
|
+
const parentFilter = 'railcontent_id in [...(^.parent_content_data[].id)]'
|
|
1310
1310
|
const statusFilter = "&& status in ['scheduled', 'published', 'archived', 'unlisted']"
|
|
1311
1311
|
|
|
1312
1312
|
const query = `*[railcontent_id == ${railcontentId}]{
|
|
@@ -1379,7 +1379,7 @@ export async function fetchHierarchy(railcontentId) {
|
|
|
1379
1379
|
}
|
|
1380
1380
|
|
|
1381
1381
|
function populateHierarchyLookups(currentLevel, data, parentId) {
|
|
1382
|
-
const railcontentIdField = currentLevel.railcontent_id ?
|
|
1382
|
+
const railcontentIdField = currentLevel.railcontent_id ? 'railcontent_id' : 'id'
|
|
1383
1383
|
|
|
1384
1384
|
let contentId = currentLevel[railcontentIdField]
|
|
1385
1385
|
let children = currentLevel['children']
|
|
@@ -1534,6 +1534,10 @@ function contentResultsDecorator(results, fieldName, callback) {
|
|
|
1534
1534
|
results.related_lessons.forEach((result) => {
|
|
1535
1535
|
result[fieldName] = callback(result)
|
|
1536
1536
|
})
|
|
1537
|
+
} else if (results.data && Array.isArray(results.data)) {
|
|
1538
|
+
results.data.forEach((result) => {
|
|
1539
|
+
result[fieldName] = callback(result)
|
|
1540
|
+
})
|
|
1537
1541
|
} else {
|
|
1538
1542
|
results[fieldName] = callback(results)
|
|
1539
1543
|
}
|
|
@@ -2017,7 +2021,7 @@ export async function fetchMethodV2Structure(brand) {
|
|
|
2017
2021
|
* @returns {Promise<*|null>}
|
|
2018
2022
|
*/
|
|
2019
2023
|
export async function fetchMethodV2StructureFromId(contentId) {
|
|
2020
|
-
const _type =
|
|
2024
|
+
const _type = 'method-v2'
|
|
2021
2025
|
const query = `*[_type == '${_type}' && brand == *[railcontent_id == ${contentId}][0].brand][0...1]{
|
|
2022
2026
|
'sanity_id': _id,
|
|
2023
2027
|
brand,
|
|
@@ -2028,7 +2032,7 @@ export async function fetchMethodV2StructureFromId(contentId) {
|
|
|
2028
2032
|
'children': child[]->railcontent_id
|
|
2029
2033
|
}
|
|
2030
2034
|
}`
|
|
2031
|
-
return await fetchSanity(query, false)
|
|
2035
|
+
return await fetchSanity(query, false)
|
|
2032
2036
|
}
|
|
2033
2037
|
|
|
2034
2038
|
/**
|