musora-content-services 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.github/workflows/conventional-commits.yaml +0 -0
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +1 -2
  4. package/docs/ContentOrganization.html +0 -0
  5. package/docs/Gamification.html +0 -0
  6. package/docs/UserManagementSystem.html +0 -0
  7. package/docs/api_types.js.html +0 -0
  8. package/docs/config.js.html +0 -0
  9. package/docs/content-org_content-org.js.html +0 -0
  10. package/docs/content-org_playlists-types.js.html +0 -0
  11. package/docs/content-org_playlists.js.html +0 -0
  12. package/docs/content.js.html +0 -0
  13. package/docs/gamification_awards.js.html +0 -0
  14. package/docs/gamification_gamification.js.html +0 -0
  15. package/docs/gamification_types.js.html +0 -0
  16. package/docs/global.html +0 -0
  17. package/docs/index.html +0 -0
  18. package/docs/module-Awards.html +0 -0
  19. package/docs/module-Config.html +0 -0
  20. package/docs/module-Content-Services-V2.html +0 -0
  21. package/docs/module-Interests.html +0 -0
  22. package/docs/module-Permissions.html +0 -0
  23. package/docs/module-Playlists.html +0 -0
  24. package/docs/module-Railcontent-Services.html +0 -0
  25. package/docs/module-Sanity-Services.html +0 -0
  26. package/docs/module-Sessions.html +0 -0
  27. package/docs/module-UserActivity.html +0 -0
  28. package/docs/module-UserManagement.html +0 -0
  29. package/docs/module-UserNotifications.html +0 -0
  30. package/docs/module-UserProfile.html +0 -0
  31. package/docs/railcontent.js.html +0 -0
  32. package/docs/sanity.js.html +0 -0
  33. package/docs/userActivity.js.html +0 -0
  34. package/docs/user_interests.js.html +0 -0
  35. package/docs/user_management.js.html +0 -0
  36. package/docs/user_notifications.js.html +0 -0
  37. package/docs/user_permissions.js.html +0 -0
  38. package/docs/user_profile.js.html +0 -0
  39. package/docs/user_sessions.js.html +0 -0
  40. package/docs/user_types.js.html +0 -0
  41. package/docs/user_user-management-system.js.html +0 -0
  42. package/package.json +1 -1
  43. package/src/index.d.ts +2 -0
  44. package/src/index.js +2 -0
  45. package/src/services/content.js +1 -1
  46. package/src/services/contentProgress.js +35 -1
  47. package/src/services/imageSRCBuilder.js +0 -0
  48. package/src/services/railcontent.js +0 -0
  49. package/src/services/sanity.js +70 -33
  50. package/src/services/user/notifications.js +4 -3
  51. package/src/services/userActivity.js +0 -0
File without changes
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
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.12.0](https://github.com/railroadmedia/musora-content-services/compare/v2.11.0...v2.12.0) (2025-06-25)
6
+
7
+
8
+ ### Features
9
+
10
+ * **MU2-478:** simplified version of getAllStartedOrCompleted method ([b58b97b](https://github.com/railroadmedia/musora-content-services/commit/b58b97bcf95db7ad71019848dbf5e5b508adeeb8))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **MU2-608:** Sort recent lessons by user progress ([e966ffc](https://github.com/railroadmedia/musora-content-services/commit/e966ffcd87847ee6b72d35bed84967ee8fd1db44))
16
+ * **MU2-730:** Pagination on fetchNotifications method ([37e95db](https://github.com/railroadmedia/musora-content-services/commit/37e95dbaacca06a1e408db8a5d6dead3dfb23550))
17
+ * Pagination on fetchNotifications method ([9e613b9](https://github.com/railroadmedia/musora-content-services/commit/9e613b9227bb188814ebc2d8c084c18e115fbcc3))
18
+
19
+ ### [2.11.1](https://github.com/railroadmedia/musora-content-services/compare/v2.11.0...v2.11.1) (2025-06-24)
20
+
5
21
  ## [2.11.0](https://github.com/railroadmedia/musora-content-services/compare/v2.10.0...v2.11.0) (2025-06-24)
6
22
 
7
23
 
package/README.md CHANGED
@@ -6,7 +6,6 @@ allowing you to easily retrieve, filter, and manipulate content for use in vario
6
6
 
7
7
  ## Setup
8
8
  To set up the Musora Content Services project for local development, follow these steps:
9
- - Pull the latest railenvironment `php-8-3-upgrade` branch changes
10
9
  - In the railenvironment directory, start up the container with the `./rrr.sh` command
11
10
  - Run `r setup musora-content-services`
12
11
  - Run `npm install --save-dev jest`
@@ -94,4 +93,4 @@ and having jest installed (`npm install --save-dev jest`). To run the full test
94
93
  npm test
95
94
  ```
96
95
  You can also filter down to specific tests with the `-- -t="..."` option. e.g. you can run the userContext test suite
97
- with `npm test -- -t="userContext"` or just the contentLiked test with `npm test -- -t="contentLiked"`
96
+ with `npm test -- -t="userContext"` or just the contentLiked test with `npm test -- -t="contentLiked"`
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
package/docs/global.html CHANGED
File without changes
package/docs/index.html CHANGED
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
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.d.ts CHANGED
@@ -59,6 +59,7 @@ import {
59
59
  getProgressStateByIds,
60
60
  getResumeTimeSeconds,
61
61
  getResumeTimeSecondsByIds,
62
+ getStartedOrCompletedProgressOnly,
62
63
  recordWatchSession
63
64
  } from './services/contentProgress.js';
64
65
 
@@ -419,6 +420,7 @@ declare module 'musora-content-services' {
419
420
  getResumeTimeSecondsByIds,
420
421
  getScheduleContentRows,
421
422
  getSortOrder,
423
+ getStartedOrCompletedProgressOnly,
422
424
  getTabResults,
423
425
  getTimeRemainingUntilLocal,
424
426
  getUserMonthlyStats,
package/src/index.js CHANGED
@@ -59,6 +59,7 @@ import {
59
59
  getProgressStateByIds,
60
60
  getResumeTimeSeconds,
61
61
  getResumeTimeSecondsByIds,
62
+ getStartedOrCompletedProgressOnly,
62
63
  recordWatchSession
63
64
  } from './services/contentProgress.js';
64
65
 
@@ -418,6 +419,7 @@ export {
418
419
  getResumeTimeSecondsByIds,
419
420
  getScheduleContentRows,
420
421
  getSortOrder,
422
+ getStartedOrCompletedProgressOnly,
421
423
  getTabResults,
422
424
  getTimeRemainingUntilLocal,
423
425
  getUserMonthlyStats,
@@ -19,7 +19,7 @@ import {recommendations} from "./recommendations";
19
19
 
20
20
 
21
21
  export async function getLessonContentRows (brand='drumeo', pageName = 'lessons') {
22
- let recentContentIds = await fetchRecent(brand, pageName, { progress: 'recent' });
22
+ let recentContentIds = await fetchRecent(brand, pageName, { progress: 'recent', limit: 10 });
23
23
 
24
24
  let contentRows = await getContentRows(brand, pageName);
25
25
  contentRows = Array.isArray(contentRows) ? contentRows : [];
@@ -118,7 +118,7 @@ export async function getAllStartedOrCompleted({ limit = null, onlyIds = true, b
118
118
  const isRelevantStatus =
119
119
  item[DATA_KEY_STATUS] === STATE_STARTED || item[DATA_KEY_STATUS] === STATE_COMPLETED
120
120
  const isRecent = item[DATA_KEY_LAST_UPDATED_TIME] >= oneMonthAgoInSeconds
121
- const isCorrectBrand = !brand || item.b === brand
121
+ const isCorrectBrand = !brand || !item.b || item.b === brand
122
122
  const isNotExcluded = !excludedSet.has(id)
123
123
  return isRelevantStatus && isRecent && isCorrectBrand && isNotExcluded
124
124
  })
@@ -151,6 +151,40 @@ export async function getAllStartedOrCompleted({ limit = null, onlyIds = true, b
151
151
  }
152
152
  }
153
153
 
154
+ /**
155
+ * Simplified version of `getAllStartedOrCompleted`.
156
+ *
157
+ * Fetches content IDs and progress percentages for items that were
158
+ * started or completed.
159
+ *
160
+ * @param {Object} [options={}] - Optional filtering options.
161
+ * @param {string|null} [options.brand=null] - Brand to filter by (e.g., 'drumeo').
162
+ * @returns {Promise<Object>} - A map of content ID to progress value:
163
+ * {
164
+ * [id]: progressPercentage
165
+ * }
166
+ *
167
+ * @example
168
+ * const progressMap = await getStartedOrCompletedProgressOnly({ brand: 'drumeo' });
169
+ * console.log(progressMap[123]); // => 52
170
+ */
171
+ export async function getStartedOrCompletedProgressOnly({ brand = null} = {}) {
172
+ const data = await dataContext.getData()
173
+ const result = {}
174
+
175
+ Object.entries(data).forEach(([key, item]) => {
176
+ const id = parseInt(key)
177
+ const isRelevantStatus = item[DATA_KEY_STATUS] === STATE_STARTED || item[DATA_KEY_STATUS] === STATE_COMPLETED
178
+ const isCorrectBrand = !brand || item.b === brand
179
+
180
+ if (isRelevantStatus && isCorrectBrand) {
181
+ result[id] = item?.[DATA_KEY_PROGRESS] ?? 0
182
+ }
183
+ })
184
+
185
+ return result
186
+ }
187
+
154
188
  export async function assignmentStatusCompleted(assignmentId, parentContentId) {
155
189
  await dataContext.update(
156
190
  async function (localContext) {
File without changes
File without changes
@@ -512,7 +512,7 @@ export async function fetchByRailContentId(id, contentType) {
512
512
  * .then(contents => console.log(contents))
513
513
  * .catch(error => console.error(error));
514
514
  */
515
- export async function fetchByRailContentIds(ids, contentType = undefined, brand= undefined) {
515
+ export async function fetchByRailContentIds(ids, contentType = undefined, brand = undefined) {
516
516
  if (!ids) {
517
517
  return []
518
518
  }
@@ -888,11 +888,11 @@ async function getProgressFilter(progress, progressIds) {
888
888
  return `&& !(railcontent_id in [${ids.join(',')}])`
889
889
  }
890
890
  case 'recent': {
891
- const ids = await getAllStartedOrCompleted()
891
+ const ids = progressIds !== undefined ? progressIds : await getAllStartedOrCompleted()
892
892
  return `&& (railcontent_id in [${ids.join(',')}])`
893
893
  }
894
894
  case 'incomplete': {
895
- const ids = await getAllStarted()
895
+ const ids = progressIds !== undefined ? progressIds :await getAllStarted()
896
896
  return `&& railcontent_id in [${ids.join(',')}]`
897
897
  }
898
898
  default:
@@ -1320,7 +1320,7 @@ export async function fetchLessonContent(railContentId) {
1320
1320
  })
1321
1321
  const chapterProcess = (result) => {
1322
1322
  const now = getSanityDate(new Date(), false)
1323
- if(result.live_event_start_time && result.live_event_start_time){
1323
+ if (result.live_event_start_time && result.live_event_end_time) {
1324
1324
  result.isLive = result.live_event_start_time <= now && result.live_event_end_time >= now
1325
1325
  }
1326
1326
  const chapters = result.chapters ?? []
@@ -1336,15 +1336,23 @@ export async function fetchLessonContent(railContentId) {
1336
1336
  }
1337
1337
 
1338
1338
  /**
1339
+ * Returns a list of recommended content based on the provided railContentId.
1340
+ * If no recommendations found in recsys, falls back to fetching related lessons.
1339
1341
  *
1340
1342
  * @param railContentId
1341
1343
  * @param brand
1342
1344
  * @param count
1343
- * @returns {Promise<Array<Object>|null>}
1345
+ * @returns {Promise<Array<Object>>}
1344
1346
  */
1345
1347
  export async function fetchRelatedRecommendedContent(railContentId, brand, count = 10) {
1346
1348
  const recommendedItems = await fetchSimilarItems(railContentId, brand, count)
1347
- return fetchByRailContentIds(recommendedItems)
1349
+ if (recommendedItems && recommendedItems.length > 0) {
1350
+ return fetchByRailContentIds(recommendedItems)
1351
+ }
1352
+
1353
+ return await fetchRelatedLessons(railContentId, brand).then((result) =>
1354
+ result.related_lessons?.splice(0, count)
1355
+ )
1348
1356
  }
1349
1357
 
1350
1358
  /**
@@ -1414,18 +1422,20 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
1414
1422
  */
1415
1423
  export async function fetchRelatedLessons(railContentId, brand) {
1416
1424
  const filterSameTypeAndSortOrder = await new FilterBuilder(
1417
- `_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`,
1425
+ `_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`
1418
1426
  ).buildFilter()
1419
1427
  const filterSameType = await new FilterBuilder(
1420
- `_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`,
1428
+ `_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`
1421
1429
  ).buildFilter()
1422
1430
  const filterSongSameArtist = await new FilterBuilder(
1423
- `_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`,
1431
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`
1424
1432
  ).buildFilter()
1425
1433
  const filterSongSameGenre = await new FilterBuilder(
1426
- `_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`,
1434
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`
1427
1435
  ).buildFilter()
1428
- const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`, {pullFutureContent: true}).buildFilter()
1436
+ const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`, {
1437
+ pullFutureContent: true,
1438
+ }).buildFilter()
1429
1439
  const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1430
1440
  const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, "genre": genre[]->name`
1431
1441
  const queryFieldsWithSort = queryFields + ', sort'
@@ -1446,15 +1456,15 @@ export async function fetchRelatedLessons(railContentId, brand) {
1446
1456
  let result = await fetchSanity(query, false)
1447
1457
 
1448
1458
  //there's no way in sanity to retrieve the index of an array, so we must calculate after fetch
1449
- if (result['for-calculations']['parents-list']) {
1459
+ if (result['for-calculations'] && result['for-calculations']['parents-list']) {
1450
1460
  const calc = result['for-calculations']
1451
1461
  const parentCount = calc['parents-list'].length
1452
- const currentParent = (calc['parents-list'].indexOf(result['parent_id']) + 1)
1462
+ const currentParent = calc['parents-list'].indexOf(result['parent_id']) + 1
1453
1463
  const siblingCount = calc['siblings-list'].length
1454
- const currentSibling = (calc['siblings-list'].indexOf(result['railcontent_id']) + 1)
1464
+ const currentSibling = calc['siblings-list'].indexOf(result['railcontent_id']) + 1
1455
1465
 
1456
1466
  delete result['for-calculations']
1457
- result = {...result, parentCount, currentParent, siblingCount, currentSibling}
1467
+ result = { ...result, parentCount, currentParent, siblingCount, currentSibling }
1458
1468
  return result
1459
1469
  } else {
1460
1470
  delete result['for-calculations']
@@ -1504,7 +1514,7 @@ export async function fetchPackAll(railcontentId, type = 'pack') {
1504
1514
  }
1505
1515
 
1506
1516
  export async function fetchLiveEvent(brand, forcedContentId = null) {
1507
- const LIVE_EXTRA_MINUTES = 30;
1517
+ const LIVE_EXTRA_MINUTES = 30
1508
1518
  //calendarIDs taken from addevent.php
1509
1519
  // TODO import instructor calendars to Sanity
1510
1520
  let defaultCalendarID = ''
@@ -1526,8 +1536,10 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1526
1536
  }
1527
1537
  let startDateTemp = new Date()
1528
1538
  let endDateTemp = new Date()
1529
-
1530
- startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + LIVE_EXTRA_MINUTES))
1539
+
1540
+ startDateTemp = new Date(
1541
+ startDateTemp.setMinutes(startDateTemp.getMinutes() + LIVE_EXTRA_MINUTES)
1542
+ )
1531
1543
  endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - LIVE_EXTRA_MINUTES))
1532
1544
 
1533
1545
  // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
@@ -2175,12 +2187,12 @@ async function buildQuery(
2175
2187
  function buildEntityAndTotalQuery(
2176
2188
  filter = '',
2177
2189
  fields = '...',
2178
- { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
2190
+ { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false, withoutPagination = false }
2179
2191
  ) {
2180
- const sortString = sortOrder ? `order(${sortOrder})` : ''
2181
- const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
2192
+ const sortString = sortOrder ? ` | order(${sortOrder})` : ''
2193
+ const countString = isSingle ? '[0...1]' : (withoutPagination ? ``: `[${start}...${end}]`)
2182
2194
  const query = `{
2183
- "entity": *[${filter}] | ${sortString}${countString}
2195
+ "entity": *[${filter}] ${sortString}${countString}
2184
2196
  {
2185
2197
  ${fields}
2186
2198
  },
@@ -2299,17 +2311,32 @@ export async function fetchTabData(
2299
2311
  ) {
2300
2312
  const start = (page - 1) * limit
2301
2313
  const end = start + limit
2302
-
2314
+ let withoutPagination = false
2303
2315
  // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
2304
2316
  const includedFieldsFilter =
2305
2317
  includedFields.length > 0 ? filtersToGroq(includedFields, [], pageName) : ''
2306
2318
 
2319
+ let sortOrder = getSortOrder(sort, brand, '')
2320
+
2321
+ switch (progress) {
2322
+ case 'recent':
2323
+ progressIds = await getAllStartedOrCompleted({ brand, onlyIds: true });
2324
+ sortOrder = null;
2325
+ withoutPagination = true;
2326
+ break;
2327
+ case 'incomplete':
2328
+ progressIds = await getAllStarted();
2329
+ sortOrder = null;
2330
+ break;
2331
+ case 'completed':
2332
+ progressIds = await getAllCompleted();
2333
+ sortOrder = null;
2334
+ break;
2335
+ }
2336
+
2307
2337
  // limits the results to supplied progressIds for started & completed filters
2308
2338
  const progressFilter = await getProgressFilter(progress, progressIds)
2309
2339
 
2310
- // Determine the sort order
2311
- const sortOrder = getSortOrder(sort, brand, '')
2312
-
2313
2340
  let fields = DEFAULT_FIELDS
2314
2341
  let fieldsString = fields.join(',')
2315
2342
 
@@ -2336,9 +2363,23 @@ export async function fetchTabData(
2336
2363
  sortOrder: sortOrder,
2337
2364
  start: start,
2338
2365
  end: end,
2366
+ withoutPagination: withoutPagination,
2339
2367
  })
2340
2368
 
2341
- return fetchSanity(query, true)
2369
+ let results = await fetchSanity(query, true);
2370
+
2371
+ if (['recent', 'incomplete', 'completed'].includes(progress) && results.entity.length > 1) {
2372
+ const orderMap = new Map(progressIds.map((id, index) => [id, index]))
2373
+ results.entity = results.entity
2374
+ .sort((a, b) => {
2375
+ const aIdx = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
2376
+ const bIdx = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
2377
+ return aIdx - bIdx || new Date(b.published_on) - new Date(a.published_on);
2378
+ })
2379
+ .slice(start, end);
2380
+ }
2381
+
2382
+ return results;
2342
2383
  }
2343
2384
 
2344
2385
  export async function fetchRecent(
@@ -2394,17 +2435,13 @@ export async function fetchScheduledAndNewReleases(
2394
2435
  return fetchSanity(query, true)
2395
2436
  }
2396
2437
 
2397
- export async function fetchShows(
2398
- brand,
2399
- type,
2400
- sort = 'sort'
2401
- ) {
2438
+ export async function fetchShows(brand, type, sort = 'sort') {
2402
2439
  const sortOrder = getSortOrder(sort, brand)
2403
2440
  const filter = `_type == '${type}' && brand == '${brand}'`
2404
2441
  const filterParams = {}
2405
2442
 
2406
2443
  const query = await buildQuery(filter, filterParams, getFieldsForContentType(type), {
2407
- sortOrder: sortOrder
2444
+ sortOrder: sortOrder,
2408
2445
  })
2409
2446
  return fetchSanity(query, true)
2410
2447
  }
@@ -12,6 +12,7 @@ const baseUrl = `/api/notifications`
12
12
  * @param {Object} [options={}] - Options for fetching notifications.
13
13
  * @param {string} options.brand - The brand to filter notifications by. (Required)
14
14
  * @param {number} [options.limit=10] - The maximum number of notifications to fetch.
15
+ * @param {number} [options.page=1] - The page number for pagination.
15
16
  * @param {boolean} [options.onlyUnread=false] - Whether to fetch only unread notifications. If true, adds `unread=1` to the query.
16
17
  *
17
18
  * @returns {Promise<Array<Object>>} - A promise that resolves to an array of notifications.
@@ -19,17 +20,17 @@ const baseUrl = `/api/notifications`
19
20
  * @throws {Error} - Throws an error if the brand is not provided.
20
21
  *
21
22
  * @example
22
- * fetchNotifications({ brand: 'drumeo', limit: 5, onlyUnread: true })
23
+ * fetchNotifications({ brand: 'drumeo', limit: 5, onlyUnread: true, page: 2 })
23
24
  * .then(notifications => console.log(notifications))
24
25
  * .catch(error => console.error(error));
25
26
  */
26
- export async function fetchNotifications({ brand = null, limit = 10, onlyUnread = false } = {}) {
27
+ export async function fetchNotifications({ brand = null, limit = 10, onlyUnread = false, page = 1 } = {}) {
27
28
  if (!brand) {
28
29
  throw new Error('brand is required')
29
30
  }
30
31
 
31
32
  const unreadParam = onlyUnread ? '&unread=1' : ''
32
- const url = `${baseUrl}/v1?brand=${brand}${unreadParam}&limit=${limit}`
33
+ const url = `${baseUrl}/v1?brand=${brand}${unreadParam}&limit=${limit}&page=${page}`
33
34
  return fetchHandler(url, 'get')
34
35
  }
35
36
 
File without changes