musora-content-services 2.3.7 → 2.3.8
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 +7 -0
- package/package.json +1 -1
- package/src/index.d.ts +13 -0
- package/src/index.js +13 -0
- package/src/services/dateUtils.js +55 -0
- package/src/services/railcontent.js +25 -26
- package/src/services/recommendations.js +10 -9
- package/src/services/userActivity.js +6 -34
- package/test/mockData/mockData_user_practices.json +0 -0
- package/test/streakMessage.test.js +0 -0
- package/test/userActivity.test.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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.3.8](https://github.com/railroadmedia/musora-content-services/compare/v2.3.7...v2.3.8) (2025-04-08)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **BEH-363:** Point homepage recommended endpoint to BE ([#208](https://github.com/railroadmedia/musora-content-services/issues/208)) ([16d3308](https://github.com/railroadmedia/musora-content-services/commit/16d3308a80c68d3fedfc75c42afa9cb2bb9ddfd5))
|
|
11
|
+
|
|
5
12
|
### [2.3.7](https://github.com/railroadmedia/musora-content-services/compare/v2.3.4...v2.3.7) (2025-04-07)
|
|
6
13
|
|
|
7
14
|
### [2.3.6](https://github.com/railroadmedia/musora-content-services/compare/v2.3.5...v2.3.6) (2025-04-04)
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -47,6 +47,14 @@ import {
|
|
|
47
47
|
verifyLocalDataContext
|
|
48
48
|
} from './services/dataContext.js';
|
|
49
49
|
|
|
50
|
+
import {
|
|
51
|
+
convertToTimeZone,
|
|
52
|
+
getMonday,
|
|
53
|
+
getWeekNumber,
|
|
54
|
+
isNextDay,
|
|
55
|
+
isSameDate
|
|
56
|
+
} from './services/dateUtils.js';
|
|
57
|
+
|
|
50
58
|
import {
|
|
51
59
|
getActiveDiscussions
|
|
52
60
|
} from './services/forum.js';
|
|
@@ -236,6 +244,7 @@ declare module 'musora-content-services' {
|
|
|
236
244
|
closeComment,
|
|
237
245
|
contentStatusCompleted,
|
|
238
246
|
contentStatusReset,
|
|
247
|
+
convertToTimeZone,
|
|
239
248
|
countAssignmentsAndLessons,
|
|
240
249
|
createComment,
|
|
241
250
|
createPlaylist,
|
|
@@ -332,6 +341,7 @@ declare module 'musora-content-services' {
|
|
|
332
341
|
getAllStartedOrCompleted,
|
|
333
342
|
getContentRows,
|
|
334
343
|
getLessonContentRows,
|
|
344
|
+
getMonday,
|
|
335
345
|
getNewAndUpcoming,
|
|
336
346
|
getPracticeSessions,
|
|
337
347
|
getProgressPercentage,
|
|
@@ -348,10 +358,13 @@ declare module 'musora-content-services' {
|
|
|
348
358
|
getUserMonthlyStats,
|
|
349
359
|
getUserPractices,
|
|
350
360
|
getUserWeeklyStats,
|
|
361
|
+
getWeekNumber,
|
|
351
362
|
globalConfig,
|
|
352
363
|
initializeService,
|
|
353
364
|
isBucketUrl,
|
|
354
365
|
isContentLiked,
|
|
366
|
+
isNextDay,
|
|
367
|
+
isSameDate,
|
|
355
368
|
jumpToContinueContent,
|
|
356
369
|
likeComment,
|
|
357
370
|
likeContent,
|
package/src/index.js
CHANGED
|
@@ -47,6 +47,14 @@ import {
|
|
|
47
47
|
verifyLocalDataContext
|
|
48
48
|
} from './services/dataContext.js';
|
|
49
49
|
|
|
50
|
+
import {
|
|
51
|
+
convertToTimeZone,
|
|
52
|
+
getMonday,
|
|
53
|
+
getWeekNumber,
|
|
54
|
+
isNextDay,
|
|
55
|
+
isSameDate
|
|
56
|
+
} from './services/dateUtils.js';
|
|
57
|
+
|
|
50
58
|
import {
|
|
51
59
|
getActiveDiscussions
|
|
52
60
|
} from './services/forum.js';
|
|
@@ -235,6 +243,7 @@ export {
|
|
|
235
243
|
closeComment,
|
|
236
244
|
contentStatusCompleted,
|
|
237
245
|
contentStatusReset,
|
|
246
|
+
convertToTimeZone,
|
|
238
247
|
countAssignmentsAndLessons,
|
|
239
248
|
createComment,
|
|
240
249
|
createPlaylist,
|
|
@@ -331,6 +340,7 @@ export {
|
|
|
331
340
|
getAllStartedOrCompleted,
|
|
332
341
|
getContentRows,
|
|
333
342
|
getLessonContentRows,
|
|
343
|
+
getMonday,
|
|
334
344
|
getNewAndUpcoming,
|
|
335
345
|
getPracticeSessions,
|
|
336
346
|
getProgressPercentage,
|
|
@@ -347,10 +357,13 @@ export {
|
|
|
347
357
|
getUserMonthlyStats,
|
|
348
358
|
getUserPractices,
|
|
349
359
|
getUserWeeklyStats,
|
|
360
|
+
getWeekNumber,
|
|
350
361
|
globalConfig,
|
|
351
362
|
initializeService,
|
|
352
363
|
isBucketUrl,
|
|
353
364
|
isContentLiked,
|
|
365
|
+
isNextDay,
|
|
366
|
+
isSameDate,
|
|
354
367
|
jumpToContinueContent,
|
|
355
368
|
likeComment,
|
|
356
369
|
likeContent,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// dateUtils.js
|
|
2
|
+
|
|
3
|
+
export function convertToTimeZone(date, timeZone) {
|
|
4
|
+
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
5
|
+
timeZone,
|
|
6
|
+
year: 'numeric',
|
|
7
|
+
month: '2-digit',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
hour: '2-digit',
|
|
10
|
+
minute: '2-digit',
|
|
11
|
+
second: '2-digit',
|
|
12
|
+
hour12: false,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const parts = formatter.formatToParts(date).reduce((acc, part) => {
|
|
16
|
+
if (part.type !== 'literal') acc[part.type] = part.value;
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
19
|
+
|
|
20
|
+
return new Date(`${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}`);
|
|
21
|
+
}
|
|
22
|
+
// Get start of the week (Monday)
|
|
23
|
+
export function getMonday(date) {
|
|
24
|
+
const d = new Date(date);
|
|
25
|
+
const day = d.getDay();
|
|
26
|
+
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
|
27
|
+
return new Date(d.setDate(diff));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Get the week number
|
|
31
|
+
export function getWeekNumber(d) {
|
|
32
|
+
let newDate = new Date(d.getTime());
|
|
33
|
+
newDate.setUTCDate(newDate.getUTCDate() + 4 - (newDate.getUTCDay()||7));
|
|
34
|
+
var yearStart = new Date(Date.UTC(newDate.getUTCFullYear(),0,1));
|
|
35
|
+
var weekNo = Math.ceil(( ( (newDate - yearStart) / 86400000) + 1)/7);
|
|
36
|
+
return weekNo;
|
|
37
|
+
}
|
|
38
|
+
//Check if two dates are the same
|
|
39
|
+
export function isSameDate(date1, date2, method = '') {
|
|
40
|
+
return date1.toISOString().split('T')[0] === date2.toISOString().split('T')[0];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if two dates are consecutive days
|
|
44
|
+
export function isNextDay(prev, current) {
|
|
45
|
+
const prevDate = new Date(prev.getFullYear(), prev.getMonth(), prev.getDate());
|
|
46
|
+
const nextDate = new Date(prevDate);
|
|
47
|
+
nextDate.setDate(prevDate.getDate() + 1); // Add 1 day
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
nextDate.getFullYear() === current.getFullYear() &&
|
|
51
|
+
nextDate.getMonth() === current.getMonth() &&
|
|
52
|
+
nextDate.getDate() === current.getDate()
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { globalConfig } from './config.js'
|
|
5
5
|
import { fetchJSONHandler } from '../lib/httpHelper.js'
|
|
6
|
+
import { convertToTimeZone } from './dateUtils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Exported functions that are excluded from index generation.
|
|
@@ -1175,44 +1176,42 @@ export async function editComment(commentId, comment) {
|
|
|
1175
1176
|
}
|
|
1176
1177
|
|
|
1177
1178
|
export async function fetchUserPractices(currentVersion) {
|
|
1178
|
-
const url = `/api/user/practices/v1/practices
|
|
1179
|
-
const response = await fetchDataHandler(url, currentVersion)
|
|
1180
|
-
const { data, version } = response
|
|
1181
|
-
const userPractices = data
|
|
1179
|
+
const url = `/api/user/practices/v1/practices`;
|
|
1180
|
+
const response = await fetchDataHandler(url, currentVersion);
|
|
1181
|
+
const { data, version } = response;
|
|
1182
|
+
const userPractices = data;
|
|
1183
|
+
|
|
1184
|
+
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1182
1185
|
|
|
1183
|
-
let formattedPractices = userPractices.reduce((acc, practice) => {
|
|
1184
|
-
// Convert UTC date to user's local timezone
|
|
1185
|
-
let utcDate = new Date(practice.day); // `practice.day` is in ISO format, e.g., "2025-04-01T10:42:17.000000Z"
|
|
1186
1186
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1187
|
+
const formattedPractices = userPractices.reduce((acc, practice) => {
|
|
1188
|
+
// Convert UTC date to user's local date (still a Date object)
|
|
1189
|
+
const utcDate = new Date(practice.day);
|
|
1190
|
+
const localDate = convertToTimeZone(utcDate, userTimeZone);
|
|
1191
1191
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
(localDate.getMonth() + 1).
|
|
1195
|
-
localDate.getDate()
|
|
1192
|
+
const userTimeZoneDay =
|
|
1193
|
+
localDate.getFullYear() + '-' +
|
|
1194
|
+
String(localDate.getMonth() + 1).padStart(2, '0') + '-' +
|
|
1195
|
+
String(localDate.getDate()).padStart(2, '0');
|
|
1196
1196
|
|
|
1197
|
-
// Initialize the array if the day does not exist
|
|
1198
1197
|
if (!acc[userTimeZoneDay]) {
|
|
1199
|
-
acc[userTimeZoneDay] = []
|
|
1198
|
+
acc[userTimeZoneDay] = [];
|
|
1200
1199
|
}
|
|
1201
1200
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1201
|
+
acc[userTimeZoneDay].push({
|
|
1202
|
+
id: practice.id,
|
|
1203
|
+
duration_seconds: practice.duration_seconds,
|
|
1204
|
+
});
|
|
1204
1205
|
|
|
1205
|
-
return acc
|
|
1206
|
-
}, {})
|
|
1206
|
+
return acc;
|
|
1207
|
+
}, {});
|
|
1207
1208
|
|
|
1208
|
-
|
|
1209
|
+
return {
|
|
1209
1210
|
data: {
|
|
1210
1211
|
practices: formattedPractices,
|
|
1211
1212
|
},
|
|
1212
|
-
version
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
return json
|
|
1213
|
+
version,
|
|
1214
|
+
};
|
|
1216
1215
|
}
|
|
1217
1216
|
|
|
1218
1217
|
export async function logUserPractice(practiceDetails) {
|
|
@@ -113,16 +113,17 @@ export async function rankItems(brand, content_ids) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
export async function recommendations(brand, {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
num_recommendations: limit,
|
|
121
|
-
}
|
|
122
|
-
const url = `/recommendations/`
|
|
116
|
+
export async function recommendations(brand, { section = ''} = {}) {
|
|
117
|
+
section = section.toUpperCase().replace('-', '_')
|
|
118
|
+
const sectionString = section ? `§ion=${section}` : '';
|
|
119
|
+
const url = `/api/content/v1/recommendations?brand=${brand}${sectionString}`
|
|
123
120
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
return fetchJSONHandler(
|
|
122
|
+
url,
|
|
123
|
+
globalConfig.railcontentConfig.token,
|
|
124
|
+
globalConfig.railcontentConfig.baseUrl,
|
|
125
|
+
'get'
|
|
126
|
+
)
|
|
126
127
|
} catch (error) {
|
|
127
128
|
console.error('Fetch error:', error)
|
|
128
129
|
return null
|
|
@@ -6,6 +6,7 @@ import {fetchUserPractices, logUserPractice, fetchUserPracticeMeta, fetchHandler
|
|
|
6
6
|
import { DataContext, UserActivityVersionKey } from './dataContext.js'
|
|
7
7
|
import {fetchByRailContentIds} from "./sanity";
|
|
8
8
|
import {lessonTypesMapping} from "../contentTypeConfig";
|
|
9
|
+
import { convertToTimeZone, getMonday, getWeekNumber, isSameDate, isNextDay } from './dateUtils.js';
|
|
9
10
|
|
|
10
11
|
const recentActivity = [
|
|
11
12
|
{ id: 5,title: '3 Easy Classical Songs For Beginners', action: 'Comment', thumbnail: 'https://cdn.sanity.io/images/4032r8py/production/8a7fb4d7473306c5fa51ba2e8867e03d44342b18-1920x1080.jpg', summary: 'Just completed the advanced groove lesson! I’m finally feeling more confident with my fills. Thanks for the clear explanations and practice tips! ', date: '2025-03-25 10:09:48' },
|
|
@@ -408,7 +409,10 @@ export async function getPracticeSessions(day) {
|
|
|
408
409
|
return null;
|
|
409
410
|
};
|
|
410
411
|
|
|
412
|
+
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
413
|
+
|
|
411
414
|
const formattedMeta = meta.data.map(practice => {
|
|
415
|
+
const utcDate = new Date(practice.created_at);
|
|
412
416
|
const content = contents.find(c => c.id === practice.content_id) || {};
|
|
413
417
|
return {
|
|
414
418
|
id: practice.id,
|
|
@@ -422,6 +426,7 @@ export async function getPracticeSessions(day) {
|
|
|
422
426
|
content_type: getFormattedType(content.type || ''),
|
|
423
427
|
content_id: practice.content_id || null,
|
|
424
428
|
content_brand: content.brand || null,
|
|
429
|
+
created_at: convertToTimeZone(utcDate, userTimeZone)
|
|
425
430
|
};
|
|
426
431
|
});
|
|
427
432
|
return { data: { practices: formattedMeta, practiceDuration } };
|
|
@@ -461,37 +466,6 @@ function buildQueryString(ids, paramName = 'practice_ids') {
|
|
|
461
466
|
return '?' + ids.map(id => `${paramName}[]=${id}`).join('&');
|
|
462
467
|
}
|
|
463
468
|
|
|
464
|
-
|
|
465
|
-
// Helper: Get start of the week (Monday)
|
|
466
|
-
function getMonday(d) {
|
|
467
|
-
d = new Date(d)
|
|
468
|
-
var day = d.getDay(),
|
|
469
|
-
diff = d.getDate() - day + (day == 0 ? -6 : 1) // adjust when day is sunday
|
|
470
|
-
return new Date(d.setDate(diff))
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Helper: Get the week number
|
|
474
|
-
function getWeekNumber(d) {
|
|
475
|
-
let newDate = new Date(d.getTime());
|
|
476
|
-
newDate.setUTCDate(newDate.getUTCDate() + 4 - (newDate.getUTCDay()||7));
|
|
477
|
-
var yearStart = new Date(Date.UTC(newDate.getUTCFullYear(),0,1));
|
|
478
|
-
var weekNo = Math.ceil(( ( (newDate - yearStart) / 86400000) + 1)/7);
|
|
479
|
-
return weekNo;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Helper: function to check if two dates are consecutive days
|
|
483
|
-
function isNextDay(prev, current) {
|
|
484
|
-
const prevDate = new Date(prev.getFullYear(), prev.getMonth(), prev.getDate());
|
|
485
|
-
const nextDate = new Date(prevDate);
|
|
486
|
-
nextDate.setDate(prevDate.getDate() + 1); // Add 1 day
|
|
487
|
-
|
|
488
|
-
return (
|
|
489
|
-
nextDate.getFullYear() === current.getFullYear() &&
|
|
490
|
-
nextDate.getMonth() === current.getMonth() &&
|
|
491
|
-
nextDate.getDate() === current.getDate()
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
469
|
// Helper: Calculate streaks
|
|
496
470
|
function calculateStreaks(practices, includeStreakMessage = false) {
|
|
497
471
|
let currentDailyStreak = 0;
|
|
@@ -584,9 +558,7 @@ function calculateStreaks(practices, includeStreakMessage = false) {
|
|
|
584
558
|
return { currentDailyStreak, currentWeeklyStreak, streakMessage };
|
|
585
559
|
}
|
|
586
560
|
|
|
587
|
-
|
|
588
|
-
return date1.toISOString().split('T')[0] === date2.toISOString().split('T')[0];
|
|
589
|
-
}
|
|
561
|
+
|
|
590
562
|
|
|
591
563
|
|
|
592
564
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|