@websolutespa/bom-mixer-store 1.7.7 → 1.7.9

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @websolutespa/bom-mixer-store
2
2
 
3
+ ## 1.7.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Added: MenuCategory strategy
8
+ - Updated dependencies
9
+ - @websolutespa/bom-core@1.7.16
10
+
11
+ ## 1.7.8
12
+
13
+ ### Patch Changes
14
+
15
+ - Modified: RouteService, MenuService to handle isHidden Category segments
16
+ - Updated dependencies
17
+ - @websolutespa/bom-core@1.7.12
18
+
3
19
  ## 1.7.7
4
20
 
5
21
  ### Patch Changes
package/dist/index.js CHANGED
@@ -184,6 +184,7 @@ async function fromCache(callback, key) {
184
184
  }
185
185
 
186
186
  // src/decorators/category.ts
187
+ var import_bom_core = require("@websolutespa/bom-core");
187
188
  async function decorateCategory_(store, item, params = {}) {
188
189
  if (typeof item.category === "string") {
189
190
  const categories = await store.category.findMany({
@@ -196,9 +197,7 @@ async function decorateCategory_(store, item, params = {}) {
196
197
  if (categories.length > 0) {
197
198
  const category = categories[0];
198
199
  const routes = await store.route.findMany();
199
- const route = routes.find(
200
- (x) => x.market === params.market && x.locale === params.locale && x.isDefault && x.category === category.id
201
- );
200
+ const route = (0, import_bom_core.findRouteOfCategory)(routes, category.id, params.market, params.locale);
202
201
  if (route) {
203
202
  category.href = route.id;
204
203
  }
@@ -216,7 +215,7 @@ async function decorateCategories_(store, items, params = {}) {
216
215
  return items.map((item) => {
217
216
  const category = categories.find((x) => x.id === item.category);
218
217
  if (category) {
219
- const route = routes.find((x) => x.isDefault && x.category === category.id);
218
+ const route = (0, import_bom_core.findRouteOfCategory)(routes, category.id);
220
219
  if (route) {
221
220
  category.href = route.id;
222
221
  }
@@ -278,25 +277,25 @@ async function decorateHrefs_(store, items, params = {}) {
278
277
  }
279
278
 
280
279
  // src/decorators/localizable.ts
281
- var import_bom_core = require("@websolutespa/bom-core");
280
+ var import_bom_core2 = require("@websolutespa/bom-core");
282
281
  async function decorateLocalizable_(item, params = {}) {
283
282
  if (params.locale) {
284
- return (0, import_bom_core.localizeItem)(item, params.locale);
283
+ return (0, import_bom_core2.localizeItem)(item, params.locale);
285
284
  } else {
286
- return (0, import_bom_core.cloneItem)(item);
285
+ return (0, import_bom_core2.cloneItem)(item);
287
286
  }
288
287
  }
289
288
  async function decorateLocalizables_(items, params = {}) {
290
289
  const decoratedItems = [];
291
290
  for (const item of items) {
292
- const decoratedItem = params.locale ? (0, import_bom_core.localizeItem)(item, params.locale) : (0, import_bom_core.cloneItem)(item);
291
+ const decoratedItem = params.locale ? (0, import_bom_core2.localizeItem)(item, params.locale) : (0, import_bom_core2.cloneItem)(item);
293
292
  decoratedItems.push(decoratedItem);
294
293
  }
295
294
  return decoratedItems;
296
295
  }
297
296
 
298
297
  // src/decorators/menu.ts
299
- var import_bom_core2 = require("@websolutespa/bom-core");
298
+ var import_bom_core3 = require("@websolutespa/bom-core");
300
299
  async function decorateMenu_(store, item, params) {
301
300
  const { market, locale } = params;
302
301
  const items = itemsFilteredByMarket_(item.items || [], market);
@@ -350,7 +349,7 @@ function itemHasMarket_(item, market) {
350
349
  }
351
350
  function collectUndecoratedCategories(items, undecoratedCategories = []) {
352
351
  items.forEach((item) => {
353
- if ((0, import_bom_core2.isUndecoratedMenuCategory)(item)) {
352
+ if ((0, import_bom_core3.isUndecoratedMenuCategory)(item)) {
354
353
  undecoratedCategories.push(item);
355
354
  }
356
355
  if (item.items) {
@@ -361,7 +360,7 @@ function collectUndecoratedCategories(items, undecoratedCategories = []) {
361
360
  }
362
361
  function collectUndecoratedPages(items, undecoratedPages = []) {
363
362
  items.forEach((item) => {
364
- if ((0, import_bom_core2.isUndecoratedMenuPage)(item)) {
363
+ if ((0, import_bom_core3.isUndecoratedMenuPage)(item)) {
365
364
  undecoratedPages.push(item);
366
365
  }
367
366
  if (item.items) {
@@ -371,26 +370,44 @@ function collectUndecoratedPages(items, undecoratedPages = []) {
371
370
  return undecoratedPages;
372
371
  }
373
372
  var MAX_INT = 1e12;
374
- function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
375
- const decoratedItem = {
376
- abstract: item.abstract,
377
- category: "",
378
- customClass: item.customClass,
373
+ function makeCategoryMenuItem_(item, category) {
374
+ const id = item.id || (0, import_bom_core3.uuidv4)();
375
+ const title = item.title || category.title || "";
376
+ const abstract = item.abstract || void 0;
377
+ const customClass = item.customClass || void 0;
378
+ const media = item.media || category.media;
379
+ const items = [];
380
+ return category.isHidden ? {
381
+ type: "group",
382
+ id,
383
+ title,
384
+ abstract,
385
+ media,
386
+ customClass,
387
+ items
388
+ } : {
389
+ type: "category",
390
+ category: category.id,
391
+ id,
379
392
  href: "",
380
- id: item.id || "",
381
- items: [],
382
- title: "",
383
- type: item.type
393
+ title,
394
+ abstract,
395
+ media,
396
+ customClass,
397
+ items
384
398
  };
385
- const category = typeof item.category === "string" ? categories.find((x) => x.id === item.category) : item.category;
399
+ }
400
+ function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
401
+ const strategy = item.strategy || import_bom_core3.MenuCategoryStrategy.Default;
402
+ const categoryId = (0, import_bom_core3.asCategoryId)(item.category);
403
+ const category = categoryId ? categories.find((x) => x.id === categoryId) : void 0;
386
404
  if (category) {
387
- decoratedItem.category = category.id;
388
- decoratedItem.title = item.title || category.title || "";
389
- decoratedItem.media = decoratedItem.media || category.media;
390
- const route = routes.find((x) => x.isDefault && x.category === category.id);
391
- if (route) {
392
- decoratedItem.href = route.id;
393
- } else {
405
+ const decoratedItem = makeCategoryMenuItem_(item, category);
406
+ if (!category.isHidden) {
407
+ const route = routes.find((route2) => route2.isDefault && route2.resolvedCategory === category.id);
408
+ if (route) {
409
+ decoratedItem.href = route.id;
410
+ }
394
411
  }
395
412
  let maxDepth = item.maxDepth;
396
413
  if (typeof maxDepth === "number") {
@@ -399,31 +416,55 @@ function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
399
416
  }
400
417
  maxDepth--;
401
418
  }
402
- const childCategories = categories.filter((x) => x.category && (typeof x.category === "object" ? x.category.id === category.id : x.category === category.id));
403
- if (childCategories.length) {
404
- childCategories.sort((a, b) => {
405
- return (a.order || MAX_INT) - (b.order || MAX_INT);
406
- });
407
- decoratedItem.items = childCategories.map((c) => decorateMenuCategory_({
408
- id: c.id,
409
- type: "category",
410
- category: c.id,
411
- maxDepth,
412
- items: []
413
- }, categories, routes, market, depth + 1));
414
- } else {
415
- const childRoutes = routes.filter((x) => !x.isDefault && x.category === category.id);
416
- childRoutes.sort((a, b) => {
417
- return (a.order || MAX_INT) - (b.order || MAX_INT);
418
- });
419
- decoratedItem.items = childRoutes.map((x) => decorateMenuRoute_(x));
420
- }
419
+ const childCategories = (category.id ? categories.filter((x) => (0, import_bom_core3.asCategoryId)(x.category) === category.id) : []).filter((x) => {
420
+ switch (strategy) {
421
+ case import_bom_core3.MenuCategoryStrategy.ExcludeHidden:
422
+ return Boolean(x.isHidden) === false;
423
+ case import_bom_core3.MenuCategoryStrategy.Default:
424
+ return true;
425
+ }
426
+ });
427
+ childCategories.sort((a, b) => {
428
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
429
+ });
430
+ const subStrategy = strategy;
431
+ const childCategoryItems = childCategories.map((c) => decorateMenuCategory_({
432
+ id: c.id,
433
+ type: "category",
434
+ category: c.id,
435
+ maxDepth,
436
+ strategy: subStrategy,
437
+ // dispatch sub strategy
438
+ items: []
439
+ }, categories, routes, market, depth + 1));
440
+ const childRoutes = routes.filter((route) => {
441
+ switch (strategy) {
442
+ case import_bom_core3.MenuCategoryStrategy.ExcludeHidden:
443
+ return route.resolvedCategory === category.id && Boolean(route.isDefault) === false;
444
+ case import_bom_core3.MenuCategoryStrategy.Default:
445
+ return route.category === category.id && (category.isHidden || Boolean(route.isDefault) === false);
446
+ }
447
+ });
448
+ childRoutes.sort((a, b) => {
449
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
450
+ });
451
+ const childRouteItems = childRoutes.map((x) => decorateMenuRoute_(x));
452
+ decoratedItem.items = [
453
+ ...childCategoryItems,
454
+ ...childRouteItems
455
+ ].filter((x) => Boolean(x));
456
+ return decoratedItem;
421
457
  }
422
- return decoratedItem;
458
+ return {
459
+ id: item.id || (0, import_bom_core3.uuidv4)(),
460
+ type: "group",
461
+ title: item.title || "",
462
+ items: []
463
+ };
423
464
  }
424
465
  function decorateMenuRoute_(item) {
425
466
  const decoratedItem = {
426
- id: item.page,
467
+ id: (0, import_bom_core3.uuidv4)(),
427
468
  type: "route",
428
469
  title: item.title,
429
470
  href: item.id,
@@ -435,13 +476,13 @@ function decorateMenuRoute_(item) {
435
476
  return decoratedItem;
436
477
  }
437
478
  async function decorateMenuPage_(item, routes, store, params = {}) {
438
- const id = (0, import_bom_core2.isEquatable)(item.page.value) ? item.page.value : item.page.value.id;
479
+ const id = (0, import_bom_core3.isEquatable)(item.page.value) ? item.page.value : item.page.value.id;
439
480
  const route = routes.find(
440
481
  (x) => x.schema === item.page.relationTo && x.page === id
441
482
  );
442
483
  const page = await store[item.page.relationTo].findOne(id, { ...params });
443
484
  const decoratedItem = {
444
- id: item.id || "",
485
+ id: item.id || (0, import_bom_core3.uuidv4)(),
445
486
  type: item.type,
446
487
  title: item.title || page?.title || "",
447
488
  abstract: item.abstract || page?.abstract || "",
@@ -456,7 +497,7 @@ async function decorateMenuPage_(item, routes, store, params = {}) {
456
497
  }
457
498
 
458
499
  // src/decorators/nav.ts
459
- var import_bom_core3 = require("@websolutespa/bom-core");
500
+ var import_bom_core4 = require("@websolutespa/bom-core");
460
501
  async function decorateNav_(store, item, params = {}, routes) {
461
502
  if (!routes) {
462
503
  routes = await store.route.findMany({
@@ -486,12 +527,12 @@ async function decorateNavs_(store, items, params = {}) {
486
527
  return items;
487
528
  }
488
529
  function collectNavs_(item, navs = []) {
489
- if ((0, import_bom_core3.isUndecoratedNav)(item)) {
530
+ if ((0, import_bom_core4.isUndecoratedNav)(item)) {
490
531
  navs.push(item);
491
532
  }
492
- if ((0, import_bom_core3.isArray)(item)) {
533
+ if ((0, import_bom_core4.isArray)(item)) {
493
534
  item.forEach((x) => collectNavs_(x, navs));
494
- } else if ((0, import_bom_core3.isObject)(item)) {
535
+ } else if ((0, import_bom_core4.isObject)(item)) {
495
536
  Object.values(item).forEach((value) => {
496
537
  collectNavs_(value, navs);
497
538
  });
@@ -521,7 +562,7 @@ function decorateNav__(item, routes) {
521
562
  }
522
563
 
523
564
  // src/decorators/optimize.ts
524
- var import_bom_core4 = require("@websolutespa/bom-core");
565
+ var import_bom_core5 = require("@websolutespa/bom-core");
525
566
  async function optimizeOne_(item) {
526
567
  const validEntries = Object.entries(item).filter(([key, value]) => {
527
568
  switch (key) {
@@ -529,7 +570,7 @@ async function optimizeOne_(item) {
529
570
  case "meta":
530
571
  return false;
531
572
  default:
532
- if ((0, import_bom_core4.isUnresolvedRelation)(value) || Array.isArray(value) && value.reduce((p, c) => p && (0, import_bom_core4.isUnresolvedRelation)(c), true)) {
573
+ if ((0, import_bom_core5.isUnresolvedRelation)(value) || Array.isArray(value) && value.reduce((p, c) => p && (0, import_bom_core5.isUnresolvedRelation)(c), true)) {
533
574
  return false;
534
575
  }
535
576
  }
@@ -547,7 +588,7 @@ async function optimizeMany_(items) {
547
588
  }
548
589
 
549
590
  // src/decorators/relation.ts
550
- var import_bom_core5 = require("@websolutespa/bom-core");
591
+ var import_bom_core6 = require("@websolutespa/bom-core");
551
592
  var SERVICE_DEFAULT_DEPTH = 3;
552
593
  async function decorateRelation_(store, item, params = {}) {
553
594
  const depth = params.depth != null ? params.depth : SERVICE_DEFAULT_DEPTH;
@@ -564,7 +605,7 @@ async function decorateRelations_(store, items, params = {}) {
564
605
  return items;
565
606
  }
566
607
  async function collectRelations_(context, item, relations = []) {
567
- if ((0, import_bom_core5.isUnresolvedRelation)(item)) {
608
+ if ((0, import_bom_core6.isUnresolvedRelation)(item)) {
568
609
  const index = relations.findIndex(
569
610
  (x) => x.relationTo === item.relationTo && x.value === item.value
570
611
  );
@@ -593,13 +634,13 @@ async function collectRelations_(context, item, relations = []) {
593
634
  } else {
594
635
  return relations[index].resolvedValue;
595
636
  }
596
- } else if ((0, import_bom_core5.isArray)(item)) {
637
+ } else if ((0, import_bom_core6.isArray)(item)) {
597
638
  const items = [];
598
639
  for (let i = 0; i < item.length; i++) {
599
640
  items[i] = await collectRelations_(context, item[i], relations);
600
641
  }
601
642
  return items;
602
- } else if ((0, import_bom_core5.isObject)(item)) {
643
+ } else if ((0, import_bom_core6.isObject)(item)) {
603
644
  for (const [key, value] of Object.entries(item)) {
604
645
  item[key] = await collectRelations_(context, value, relations);
605
646
  }
@@ -618,12 +659,12 @@ async function decorateSchemas_(store, items, schema) {
618
659
  }
619
660
 
620
661
  // src/local/local.api.ts
621
- var import_bom_core6 = require("@websolutespa/bom-core");
662
+ var import_bom_core7 = require("@websolutespa/bom-core");
622
663
  var API_URL = process.env.NEXT_PUBLIC_URL === "https://" ? process.env.NEXT_PUBLIC_MOCK_FALLBACK_URL : process.env.NEXT_PUBLIC_URL;
623
664
  var API_BASE = process.env.NEXT_PUBLIC_API || "/api";
624
665
  function getLocalApiUrl(pathname) {
625
- let url = `${import_bom_core6.isBrowser ? window.location.origin : API_URL}${API_BASE}${pathname}`;
626
- url = import_bom_core6.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
666
+ let url = `${import_bom_core7.isBrowser ? window.location.origin : API_URL}${API_BASE}${pathname}`;
667
+ url = import_bom_core7.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
627
668
  return url;
628
669
  }
629
670
  async function localApiFetch(pathname, options = {}, serviceOptions) {
@@ -632,9 +673,9 @@ async function localApiFetch(pathname, options = {}, serviceOptions) {
632
673
  url.searchParams.append("locale", serviceOptions.locale);
633
674
  url.searchParams.append("market", serviceOptions.market);
634
675
  }
635
- const apiOptions = (0, import_bom_core6.merge)({ ...getLocalApiRequestOptions() }, options);
676
+ const apiOptions = (0, import_bom_core7.merge)({ ...getLocalApiRequestOptions() }, options);
636
677
  console.log("localApiFetch", apiOptions.method, url.href);
637
- const apiResponse = await (0, import_bom_core6.httpFetch)(url.href, apiOptions);
678
+ const apiResponse = await (0, import_bom_core7.httpFetch)(url.href, apiOptions);
638
679
  return apiResponse;
639
680
  }
640
681
  async function localApiGet(url, options = {}, serviceOptions) {
@@ -673,8 +714,8 @@ function isApiRequest(request) {
673
714
  }
674
715
 
675
716
  // src/mock/mock.service.ts
676
- var import_bom_core7 = require("@websolutespa/bom-core");
677
- var MockService = class extends import_bom_core7.JsonService {
717
+ var import_bom_core8 = require("@websolutespa/bom-core");
718
+ var MockService = class extends import_bom_core8.JsonService {
678
719
  constructor(items, store) {
679
720
  super(items);
680
721
  this.store = store;
@@ -759,19 +800,19 @@ var MockPageService = class extends MockService {
759
800
  };
760
801
 
761
802
  // src/mock/mock.api.ts
762
- var import_bom_core8 = require("@websolutespa/bom-core");
803
+ var import_bom_core9 = require("@websolutespa/bom-core");
763
804
  var API_URL2 = process.env.NEXT_PUBLIC_URL;
764
805
  var API_BASE3 = process.env.NEXT_PUBLIC_API || "/api";
765
806
  function getMockApiUrl(pathname) {
766
807
  let url = `${API_URL2}${API_BASE3}/_mock${pathname}`;
767
- url = import_bom_core8.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
808
+ url = import_bom_core9.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
768
809
  return url;
769
810
  }
770
811
  async function mockApiFetch(pathname, options = {}) {
771
812
  const url = getMockApiUrl(pathname);
772
- const apiOptions = (0, import_bom_core8.merge)({ ...getLocalApiRequestOptions() }, options);
813
+ const apiOptions = (0, import_bom_core9.merge)({ ...getLocalApiRequestOptions() }, options);
773
814
  console.log("mockApiFetch", apiOptions.method, url);
774
- const apiResponse = await (0, import_bom_core8.httpFetch)(url, apiOptions);
815
+ const apiResponse = await (0, import_bom_core9.httpFetch)(url, apiOptions);
775
816
  return apiResponse;
776
817
  }
777
818
  async function mockApiGet(url, options = {}) {
@@ -791,7 +832,7 @@ async function mockApiDelete(url, options = {}) {
791
832
  }
792
833
 
793
834
  // src/store/store.handler.ts
794
- var import_bom_core14 = require("@websolutespa/bom-core");
835
+ var import_bom_core15 = require("@websolutespa/bom-core");
795
836
 
796
837
  // src/static/static.ts
797
838
  var staticCollections = [{
@@ -1066,7 +1107,7 @@ var KNOWN_COLLECTIONS = [
1066
1107
  var COLLECTIONS = [...ENV_COLLECTIONS, ...KNOWN_COLLECTIONS];
1067
1108
 
1068
1109
  // src/mock/mock.store.ts
1069
- var import_bom_core10 = require("@websolutespa/bom-core");
1110
+ var import_bom_core11 = require("@websolutespa/bom-core");
1070
1111
 
1071
1112
  // src/fs/fs.service.ts
1072
1113
  var fs = __toESM(require("fs"));
@@ -1094,8 +1135,8 @@ async function fsReadJson(pathname) {
1094
1135
  }
1095
1136
 
1096
1137
  // src/static/static.service.ts
1097
- var import_bom_core9 = require("@websolutespa/bom-core");
1098
- var StaticService = class extends import_bom_core9.JsonService {
1138
+ var import_bom_core10 = require("@websolutespa/bom-core");
1139
+ var StaticService = class extends import_bom_core10.JsonService {
1099
1140
  constructor(config, store) {
1100
1141
  super([]);
1101
1142
  this.loaded = false;
@@ -1123,7 +1164,7 @@ var StaticService = class extends import_bom_core9.JsonService {
1123
1164
  const config = this.config;
1124
1165
  const src = config.src;
1125
1166
  if (typeof src === "string") {
1126
- const response = await (0, import_bom_core9.httpGet)(src, {});
1167
+ const response = await (0, import_bom_core10.httpGet)(src, {});
1127
1168
  items = response.items;
1128
1169
  } else if (typeof src === "function") {
1129
1170
  if (src instanceof Promise) {
@@ -1239,7 +1280,7 @@ function getMockStoreFromData_(data) {
1239
1280
  return store;
1240
1281
  }
1241
1282
  async function getStoreData_() {
1242
- if (import_bom_core10.isDevelopment) {
1283
+ if (import_bom_core11.isDevelopment) {
1243
1284
  const path = require("path");
1244
1285
  const pathname = path.join(process.cwd(), ".mixer", "store.json");
1245
1286
  return await fsReadJson(pathname) || {};
@@ -1255,17 +1296,17 @@ async function getMockStore_() {
1255
1296
  }
1256
1297
  async function getMockStore() {
1257
1298
  return fromCache(async () => {
1258
- if (!import_bom_core10.isDevelopment) {
1299
+ if (!import_bom_core11.isDevelopment) {
1259
1300
  console.warn("getMockStore");
1260
1301
  }
1261
1302
  const store = await getMockStore_();
1262
1303
  return store;
1263
- }, import_bom_core10.isDevelopment ? void 0 : "STORE_MOCK_");
1304
+ }, import_bom_core11.isDevelopment ? void 0 : "STORE_MOCK_");
1264
1305
  }
1265
1306
 
1266
1307
  // src/cached/cached.service.ts
1267
- var import_bom_core11 = require("@websolutespa/bom-core");
1268
- var CachedService = class extends import_bom_core11.JsonService {
1308
+ var import_bom_core12 = require("@websolutespa/bom-core");
1309
+ var CachedService = class extends import_bom_core12.JsonService {
1269
1310
  constructor(service, options = {}) {
1270
1311
  super([]);
1271
1312
  this.cache_ = /* @__PURE__ */ new Map();
@@ -1304,7 +1345,7 @@ var CachedService = class extends import_bom_core11.JsonService {
1304
1345
  });
1305
1346
  }
1306
1347
  async findOne(id, params = {}) {
1307
- const serializedParams = (0, import_bom_core11.qsSerialize)(params);
1348
+ const serializedParams = (0, import_bom_core12.qsSerialize)(params);
1308
1349
  const key = `findOne[${id}]?${serializedParams}`;
1309
1350
  return await this.fromCache_(async () => {
1310
1351
  return await this.service_.findOne(id, params);
@@ -1313,7 +1354,7 @@ var CachedService = class extends import_bom_core11.JsonService {
1313
1354
  async findMany(params = {}) {
1314
1355
  if (storeStrategy === "decoratedApi" /* DecoratedApi */) {
1315
1356
  const { market, locale, ...otherParams } = params;
1316
- const serializedParams = (0, import_bom_core11.qsSerialize)({ market, locale });
1357
+ const serializedParams = (0, import_bom_core12.qsSerialize)({ market, locale });
1317
1358
  const key = `findMany?${serializedParams}`;
1318
1359
  let items = await this.fromCache_(async () => {
1319
1360
  return await this.service_.findMany({ market, locale });
@@ -1326,7 +1367,7 @@ var CachedService = class extends import_bom_core11.JsonService {
1326
1367
  }
1327
1368
  return items;
1328
1369
  } else {
1329
- const serializedParams = (0, import_bom_core11.qsSerialize)(params);
1370
+ const serializedParams = (0, import_bom_core12.qsSerialize)(params);
1330
1371
  const key = `findMany?${serializedParams}`;
1331
1372
  return await this.fromCache_(async () => {
1332
1373
  return await this.service_.findMany(params);
@@ -1334,7 +1375,7 @@ var CachedService = class extends import_bom_core11.JsonService {
1334
1375
  }
1335
1376
  }
1336
1377
  async findPaged(params = {}) {
1337
- const serializedParams = (0, import_bom_core11.qsSerialize)(params);
1378
+ const serializedParams = (0, import_bom_core12.qsSerialize)(params);
1338
1379
  const key = `findPaged?${serializedParams}`;
1339
1380
  return await this.fromCache_(async () => {
1340
1381
  return await this.service_.findPaged(params);
@@ -1352,22 +1393,22 @@ var CachedService = class extends import_bom_core11.JsonService {
1352
1393
  };
1353
1394
 
1354
1395
  // src/remote/remote.service.ts
1355
- var import_bom_core13 = require("@websolutespa/bom-core");
1396
+ var import_bom_core14 = require("@websolutespa/bom-core");
1356
1397
 
1357
1398
  // src/remote/remote.api.ts
1358
- var import_bom_core12 = require("@websolutespa/bom-core");
1399
+ var import_bom_core13 = require("@websolutespa/bom-core");
1359
1400
  var API_URL3 = process.env.STORE_URL;
1360
1401
  var API_BASE4 = process.env.STORE_API || "/api";
1361
1402
  function getRemoteApiUrl(pathname) {
1362
1403
  let url = `${API_URL3}${API_BASE4}${pathname}`;
1363
- url = import_bom_core12.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
1404
+ url = import_bom_core13.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
1364
1405
  return url;
1365
1406
  }
1366
1407
  async function remoteApiFetch(pathname, options = {}) {
1367
1408
  const url = getRemoteApiUrl(pathname);
1368
- const apiOptions = (0, import_bom_core12.merge)({ ...getRemoteApiRequestOptions() }, options);
1409
+ const apiOptions = (0, import_bom_core13.merge)({ ...getRemoteApiRequestOptions() }, options);
1369
1410
  console.log("remoteApiFetch", apiOptions.method, url);
1370
- const apiResponse = await (0, import_bom_core12.httpFetch)(url, apiOptions);
1411
+ const apiResponse = await (0, import_bom_core13.httpFetch)(url, apiOptions);
1371
1412
  return apiResponse;
1372
1413
  }
1373
1414
  async function remoteApiGet(url, options = {}) {
@@ -1404,7 +1445,7 @@ var RemoteService = class {
1404
1445
  async findOne(id, params = {}) {
1405
1446
  params.richText = String(params.richText) === "true" ? true : false;
1406
1447
  params.draft = RemoteServiceStrategy.draft;
1407
- const query = (0, import_bom_core13.qsSerialize)(params);
1448
+ const query = (0, import_bom_core14.qsSerialize)(params);
1408
1449
  const search = query ? `?${query}` : "";
1409
1450
  const url = `/${this.key}/${encodeURIComponent(id)}${search}`;
1410
1451
  const item = await remoteApiGet(url);
@@ -1417,7 +1458,7 @@ var RemoteService = class {
1417
1458
  params.richText = String(params.richText) === "true" ? true : false;
1418
1459
  params.draft = RemoteServiceStrategy.draft;
1419
1460
  params.pagination = false;
1420
- const query = (0, import_bom_core13.qsSerialize)(params);
1461
+ const query = (0, import_bom_core14.qsSerialize)(params);
1421
1462
  const search = query ? `?${query}` : "";
1422
1463
  const url = `/${this.key}${search}`;
1423
1464
  const response = await remoteApiGet(url);
@@ -1428,7 +1469,7 @@ var RemoteService = class {
1428
1469
  params.richText = String(params.richText) === "true" ? true : false;
1429
1470
  params.draft = RemoteServiceStrategy.draft;
1430
1471
  params.pagination = true;
1431
- const query = (0, import_bom_core13.qsSerialize)(params);
1472
+ const query = (0, import_bom_core14.qsSerialize)(params);
1432
1473
  const search = query ? `?${query}` : "";
1433
1474
  const url = `/${this.key}${search}`;
1434
1475
  const response = await remoteApiGet(url);
@@ -1457,7 +1498,7 @@ var RemoteService = class {
1457
1498
  }
1458
1499
  }
1459
1500
  };
1460
- const query = (0, import_bom_core13.qsSerialize)(params);
1501
+ const query = (0, import_bom_core14.qsSerialize)(params);
1461
1502
  const search = query ? `?${query}` : "";
1462
1503
  const url = `/${this.key}${search}`;
1463
1504
  const item = await remoteApiDelete(url);
@@ -1637,7 +1678,7 @@ function collectionGet(storePromise) {
1637
1678
  const collection = collections[0];
1638
1679
  const id = collections[1];
1639
1680
  const qs = Object.entries(query).map((kv) => kv.join("=")).join("&");
1640
- const params = (0, import_bom_core14.qsDeserialize)(qs);
1681
+ const params = (0, import_bom_core15.qsDeserialize)(qs);
1641
1682
  if (!COLLECTIONS.includes(collection)) {
1642
1683
  console.error("storeHandler.collectionGet", "method not allowed", collection);
1643
1684
  return response.status(405).send("method not allowed");
@@ -1649,7 +1690,7 @@ function collectionGet(storePromise) {
1649
1690
  return response.status(405).send("method not allowed");
1650
1691
  }
1651
1692
  if (id) {
1652
- const item = await service.findOne((0, import_bom_core14.asEquatable)(id), params);
1693
+ const item = await service.findOne((0, import_bom_core15.asEquatable)(id), params);
1653
1694
  if (item) {
1654
1695
  return response.status(200).json(item);
1655
1696
  } else {
@@ -1769,8 +1810,8 @@ function storeIndexHandler() {
1769
1810
  return apiHandler({
1770
1811
  get: async (request, response) => {
1771
1812
  try {
1772
- const market = import_bom_core14.defaultMarket;
1773
- const locale = import_bom_core14.defaultLocale;
1813
+ const market = import_bom_core15.defaultMarket;
1814
+ const locale = import_bom_core15.defaultLocale;
1774
1815
  if (!Array.isArray(COLLECTIONS) || COLLECTIONS?.length === 0) {
1775
1816
  console.error("apiHome.collectionGet", "collections not found", COLLECTIONS);
1776
1817
  return response.status(405).send("method not allowed");
@@ -2037,10 +2078,10 @@ async function storeApiDelete(url, options = {}) {
2037
2078
  }
2038
2079
 
2039
2080
  // src/vars/vars.handler.ts
2040
- var import_bom_core16 = require("@websolutespa/bom-core");
2081
+ var import_bom_core17 = require("@websolutespa/bom-core");
2041
2082
 
2042
2083
  // ../models/src/route/route.service.ts
2043
- var import_bom_core15 = require("@websolutespa/bom-core");
2084
+ var import_bom_core16 = require("@websolutespa/bom-core");
2044
2085
  async function getRouteLinkTree(market, locale) {
2045
2086
  const store = await getStore();
2046
2087
  const routes = await store.route.findMany({
@@ -2053,8 +2094,8 @@ async function getRouteLinkTree(market, locale) {
2053
2094
  }
2054
2095
  }
2055
2096
  });
2056
- const categories = await store.category.findMany();
2057
- const rootCategory = (0, import_bom_core15.getRootCategory)(categories);
2097
+ const categories = await store.category.findMany({ market, locale });
2098
+ const rootCategory = (0, import_bom_core16.getRootCategory)(categories);
2058
2099
  if (rootCategory) {
2059
2100
  const root = categoryToRouteLink(routes, categories, rootCategory, locale);
2060
2101
  return root;
@@ -2062,15 +2103,13 @@ async function getRouteLinkTree(market, locale) {
2062
2103
  return void 0;
2063
2104
  }
2064
2105
  function categoryToRouteLink(routes, categories, category, locale) {
2065
- const route = routes.find(
2066
- (r) => r.category === category.id && r.isDefault
2067
- );
2106
+ const route = (0, import_bom_core16.findRouteOfCategory)(routes, category.id);
2068
2107
  if (!route) {
2069
2108
  return void 0;
2070
2109
  }
2071
- const rootCategory = (0, import_bom_core15.getRootCategory)(categories);
2110
+ const rootCategory = (0, import_bom_core16.getRootCategory)(categories);
2072
2111
  const childCategories = categories.filter((x) => {
2073
- const parentId = x.category ? (0, import_bom_core15.asCategoryId)(x.category) : null;
2112
+ const parentId = (0, import_bom_core16.asCategoryId)(x.category);
2074
2113
  return rootCategory && rootCategory.id === category.id ? x.id !== rootCategory.id && (parentId === category.id || !parentId) : parentId === category.id;
2075
2114
  });
2076
2115
  const routeLink = newRouteLink(category, route, locale);
@@ -2082,7 +2121,7 @@ function newRouteLink(category, route, locale) {
2082
2121
  const id = route?.page || category.id;
2083
2122
  const media = category.media || route?.media;
2084
2123
  const schema = route?.schema || category?.schema || "unknown";
2085
- const title = (0, import_bom_core15.localize)(category.title || route?.title || "untitled", locale);
2124
+ const title = (0, import_bom_core16.localize)(category.title || route?.title || "untitled", locale);
2086
2125
  const routeLink = {
2087
2126
  category: category.id,
2088
2127
  href,
@@ -2185,10 +2224,10 @@ function varsHandler() {
2185
2224
  const { locale = defaultLocale3, market = defaultMaket } = request.query || {};
2186
2225
  const layout = typeof locale === "string" && typeof market === "string" ? await getLayout(market, locale) : void 0;
2187
2226
  const info = {
2188
- production: import_bom_core16.isProduction,
2189
- development: import_bom_core16.isDevelopment,
2190
- origin: (0, import_bom_core16.getOrigin)(request.headers),
2191
- host: (0, import_bom_core16.getHost)(request.headers),
2227
+ production: import_bom_core17.isProduction,
2228
+ development: import_bom_core17.isDevelopment,
2229
+ origin: (0, import_bom_core17.getOrigin)(request.headers),
2230
+ host: (0, import_bom_core17.getHost)(request.headers),
2192
2231
  headers: { ...sortKeys(request.headers) },
2193
2232
  env: { ...getFilteredEnv(process.env) },
2194
2233
  layout: { ...sortKeys(layout) }
package/dist/index.mjs CHANGED
@@ -70,6 +70,7 @@ async function fromCache(callback, key) {
70
70
  }
71
71
 
72
72
  // src/decorators/category.ts
73
+ import { findRouteOfCategory } from "@websolutespa/bom-core";
73
74
  async function decorateCategory_(store, item, params = {}) {
74
75
  if (typeof item.category === "string") {
75
76
  const categories = await store.category.findMany({
@@ -82,9 +83,7 @@ async function decorateCategory_(store, item, params = {}) {
82
83
  if (categories.length > 0) {
83
84
  const category = categories[0];
84
85
  const routes = await store.route.findMany();
85
- const route = routes.find(
86
- (x) => x.market === params.market && x.locale === params.locale && x.isDefault && x.category === category.id
87
- );
86
+ const route = findRouteOfCategory(routes, category.id, params.market, params.locale);
88
87
  if (route) {
89
88
  category.href = route.id;
90
89
  }
@@ -102,7 +101,7 @@ async function decorateCategories_(store, items, params = {}) {
102
101
  return items.map((item) => {
103
102
  const category = categories.find((x) => x.id === item.category);
104
103
  if (category) {
105
- const route = routes.find((x) => x.isDefault && x.category === category.id);
104
+ const route = findRouteOfCategory(routes, category.id);
106
105
  if (route) {
107
106
  category.href = route.id;
108
107
  }
@@ -183,9 +182,15 @@ async function decorateLocalizables_(items, params = {}) {
183
182
 
184
183
  // src/decorators/menu.ts
185
184
  import {
185
+ MenuCategoryStrategy,
186
+ asCategoryId,
187
+ findCategoryInCategories,
188
+ findChildCategories,
189
+ findRouteOfCategory as findRouteOfCategory2,
186
190
  isEquatable,
187
191
  isUndecoratedMenuCategory,
188
- isUndecoratedMenuPage
192
+ isUndecoratedMenuPage,
193
+ uuidv4
189
194
  } from "@websolutespa/bom-core";
190
195
  async function decorateMenu_(store, item, params) {
191
196
  const { market, locale } = params;
@@ -261,26 +266,44 @@ function collectUndecoratedPages(items, undecoratedPages = []) {
261
266
  return undecoratedPages;
262
267
  }
263
268
  var MAX_INT = 1e12;
264
- function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
265
- const decoratedItem = {
266
- abstract: item.abstract,
267
- category: "",
268
- customClass: item.customClass,
269
+ function makeCategoryMenuItem_(item, category) {
270
+ const id = item.id || uuidv4();
271
+ const title = item.title || category.title || "";
272
+ const abstract = item.abstract || void 0;
273
+ const customClass = item.customClass || void 0;
274
+ const media = item.media || category.media;
275
+ const items = [];
276
+ return category.isHidden ? {
277
+ type: "group",
278
+ id,
279
+ title,
280
+ abstract,
281
+ media,
282
+ customClass,
283
+ items
284
+ } : {
285
+ type: "category",
286
+ category: category.id,
287
+ id,
269
288
  href: "",
270
- id: item.id || "",
271
- items: [],
272
- title: "",
273
- type: item.type
289
+ title,
290
+ abstract,
291
+ media,
292
+ customClass,
293
+ items
274
294
  };
275
- const category = typeof item.category === "string" ? categories.find((x) => x.id === item.category) : item.category;
295
+ }
296
+ function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
297
+ const strategy = item.strategy || MenuCategoryStrategy.Default;
298
+ const categoryId = asCategoryId(item.category);
299
+ const category = categoryId ? categories.find((x) => x.id === categoryId) : void 0;
276
300
  if (category) {
277
- decoratedItem.category = category.id;
278
- decoratedItem.title = item.title || category.title || "";
279
- decoratedItem.media = decoratedItem.media || category.media;
280
- const route = routes.find((x) => x.isDefault && x.category === category.id);
281
- if (route) {
282
- decoratedItem.href = route.id;
283
- } else {
301
+ const decoratedItem = makeCategoryMenuItem_(item, category);
302
+ if (!category.isHidden) {
303
+ const route = routes.find((route2) => route2.isDefault && route2.resolvedCategory === category.id);
304
+ if (route) {
305
+ decoratedItem.href = route.id;
306
+ }
284
307
  }
285
308
  let maxDepth = item.maxDepth;
286
309
  if (typeof maxDepth === "number") {
@@ -289,31 +312,55 @@ function decorateMenuCategory_(item, categories, routes, market, depth = 0) {
289
312
  }
290
313
  maxDepth--;
291
314
  }
292
- const childCategories = categories.filter((x) => x.category && (typeof x.category === "object" ? x.category.id === category.id : x.category === category.id));
293
- if (childCategories.length) {
294
- childCategories.sort((a, b) => {
295
- return (a.order || MAX_INT) - (b.order || MAX_INT);
296
- });
297
- decoratedItem.items = childCategories.map((c) => decorateMenuCategory_({
298
- id: c.id,
299
- type: "category",
300
- category: c.id,
301
- maxDepth,
302
- items: []
303
- }, categories, routes, market, depth + 1));
304
- } else {
305
- const childRoutes = routes.filter((x) => !x.isDefault && x.category === category.id);
306
- childRoutes.sort((a, b) => {
307
- return (a.order || MAX_INT) - (b.order || MAX_INT);
308
- });
309
- decoratedItem.items = childRoutes.map((x) => decorateMenuRoute_(x));
310
- }
315
+ const childCategories = (category.id ? categories.filter((x) => asCategoryId(x.category) === category.id) : []).filter((x) => {
316
+ switch (strategy) {
317
+ case MenuCategoryStrategy.ExcludeHidden:
318
+ return Boolean(x.isHidden) === false;
319
+ case MenuCategoryStrategy.Default:
320
+ return true;
321
+ }
322
+ });
323
+ childCategories.sort((a, b) => {
324
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
325
+ });
326
+ const subStrategy = strategy;
327
+ const childCategoryItems = childCategories.map((c) => decorateMenuCategory_({
328
+ id: c.id,
329
+ type: "category",
330
+ category: c.id,
331
+ maxDepth,
332
+ strategy: subStrategy,
333
+ // dispatch sub strategy
334
+ items: []
335
+ }, categories, routes, market, depth + 1));
336
+ const childRoutes = routes.filter((route) => {
337
+ switch (strategy) {
338
+ case MenuCategoryStrategy.ExcludeHidden:
339
+ return route.resolvedCategory === category.id && Boolean(route.isDefault) === false;
340
+ case MenuCategoryStrategy.Default:
341
+ return route.category === category.id && (category.isHidden || Boolean(route.isDefault) === false);
342
+ }
343
+ });
344
+ childRoutes.sort((a, b) => {
345
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
346
+ });
347
+ const childRouteItems = childRoutes.map((x) => decorateMenuRoute_(x));
348
+ decoratedItem.items = [
349
+ ...childCategoryItems,
350
+ ...childRouteItems
351
+ ].filter((x) => Boolean(x));
352
+ return decoratedItem;
311
353
  }
312
- return decoratedItem;
354
+ return {
355
+ id: item.id || uuidv4(),
356
+ type: "group",
357
+ title: item.title || "",
358
+ items: []
359
+ };
313
360
  }
314
361
  function decorateMenuRoute_(item) {
315
362
  const decoratedItem = {
316
- id: item.page,
363
+ id: uuidv4(),
317
364
  type: "route",
318
365
  title: item.title,
319
366
  href: item.id,
@@ -331,7 +378,7 @@ async function decorateMenuPage_(item, routes, store, params = {}) {
331
378
  );
332
379
  const page = await store[item.page.relationTo].findOne(id, { ...params });
333
380
  const decoratedItem = {
334
- id: item.id || "",
381
+ id: item.id || uuidv4(),
335
382
  type: item.type,
336
383
  title: item.title || page?.title || "",
337
384
  abstract: item.abstract || page?.abstract || "",
@@ -1930,7 +1977,7 @@ async function storeApiDelete(url, options = {}) {
1930
1977
  import { getHost, getOrigin, isDevelopment as isDevelopment2, isProduction } from "@websolutespa/bom-core";
1931
1978
 
1932
1979
  // ../models/src/route/route.service.ts
1933
- import { asCategoryId, defaultLocale as defaultLocale2, defaultMarket as defaultMarket2, getRootCategory, localize } from "@websolutespa/bom-core";
1980
+ import { asCategoryId as asCategoryId2, defaultLocale as defaultLocale2, defaultMarket as defaultMarket2, findRouteOfCategory as findRouteOfCategory3, getRootCategory, localize } from "@websolutespa/bom-core";
1934
1981
  async function getRouteLinkTree(market, locale) {
1935
1982
  const store = await getStore();
1936
1983
  const routes = await store.route.findMany({
@@ -1943,7 +1990,7 @@ async function getRouteLinkTree(market, locale) {
1943
1990
  }
1944
1991
  }
1945
1992
  });
1946
- const categories = await store.category.findMany();
1993
+ const categories = await store.category.findMany({ market, locale });
1947
1994
  const rootCategory = getRootCategory(categories);
1948
1995
  if (rootCategory) {
1949
1996
  const root = categoryToRouteLink(routes, categories, rootCategory, locale);
@@ -1952,15 +1999,13 @@ async function getRouteLinkTree(market, locale) {
1952
1999
  return void 0;
1953
2000
  }
1954
2001
  function categoryToRouteLink(routes, categories, category, locale) {
1955
- const route = routes.find(
1956
- (r) => r.category === category.id && r.isDefault
1957
- );
2002
+ const route = findRouteOfCategory3(routes, category.id);
1958
2003
  if (!route) {
1959
2004
  return void 0;
1960
2005
  }
1961
2006
  const rootCategory = getRootCategory(categories);
1962
2007
  const childCategories = categories.filter((x) => {
1963
- const parentId = x.category ? asCategoryId(x.category) : null;
2008
+ const parentId = asCategoryId2(x.category);
1964
2009
  return rootCategory && rootCategory.id === category.id ? x.id !== rootCategory.id && (parentId === category.id || !parentId) : parentId === category.id;
1965
2010
  });
1966
2011
  const routeLink = newRouteLink(category, route, locale);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-store",
3
- "version": "1.7.7",
3
+ "version": "1.7.9",
4
4
  "description": "Mixer Store module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -15,7 +15,7 @@
15
15
  "scripts": {
16
16
  "lint": "eslint *.ts*",
17
17
  "test": "jest",
18
- "test-watch": "npm test -- --watch",
18
+ "test-watch": "npm test -- --watch --silent=false",
19
19
  "coverage:integration": "npm run test -- --coverage",
20
20
  "coverage": "run-s -c --silent coverage:*",
21
21
  "compile": "tsup ./src/index.ts --format esm,cjs --dts",
@@ -1,4 +1,4 @@
1
- import { ICategorized, ICategory, IRoute, IStore, QueryParams } from '@websolutespa/bom-core';
1
+ import { ICategorized, ICategory, IRoute, IStore, QueryParams, findRouteOfCategory } from '@websolutespa/bom-core';
2
2
 
3
3
  export async function decorateCategory_<T extends ICategorized = ICategorized>(store: IStore, item: T, params: QueryParams = {}): Promise<T | (T & { category: ICategory })> {
4
4
  if (typeof item.category === 'string') {
@@ -12,12 +12,7 @@ export async function decorateCategory_<T extends ICategorized = ICategorized>(s
12
12
  if (categories.length > 0) {
13
13
  const category = categories[0];
14
14
  const routes = await store.route.findMany<IRoute>();
15
- const route = routes.find(x =>
16
- x.market === params.market &&
17
- x.locale === params.locale &&
18
- x.isDefault &&
19
- x.category === category.id
20
- );
15
+ const route = findRouteOfCategory(routes, category.id, params.market, params.locale);
21
16
  if (route) {
22
17
  category.href = route.id;
23
18
  }
@@ -36,7 +31,7 @@ export async function decorateCategories_<T extends ICategorized = ICategorized>
36
31
  return items.map(item => {
37
32
  const category = categories.find(x => x.id === item.category);
38
33
  if (category) {
39
- const route = routes.find(x => x.isDefault && x.category === category.id);
34
+ const route = findRouteOfCategory(routes, category.id);
40
35
  if (route) {
41
36
  category.href = route.id;
42
37
  }
@@ -1,7 +1,12 @@
1
1
  import {
2
- ICategorized, ICategory, IMenu, IMenuCategory, IMenuItem, IMenuPage, IMenuRoute, IRoute,
3
- IStore, IUndecoratedMenu, IUndecoratedMenuCategory, IUndecoratedMenuItem, IUndecoratedMenuPage,
4
- QueryParams, isEquatable, isUndecoratedMenuCategory, isUndecoratedMenuPage
2
+ ICategorized, ICategory, IMenu, IMenuCategory, IMenuGroup, IMenuItem, IMenuPage, IMenuRoute, IRoute, IStore,
3
+ IUndecoratedMenu, IUndecoratedMenuCategory, IUndecoratedMenuItem, IUndecoratedMenuPage, MenuCategoryStrategy, QueryParams,
4
+ asCategoryId,
5
+ findCategoryInCategories,
6
+ findChildCategories,
7
+ findRouteOfCategory,
8
+ isEquatable, isUndecoratedMenuCategory, isUndecoratedMenuPage,
9
+ uuidv4
5
10
  } from '@websolutespa/bom-core';
6
11
 
7
12
  export async function decorateMenu_(store: IStore, item: IUndecoratedMenu, params: { market: string, locale: string }): Promise<IMenu> {
@@ -88,33 +93,150 @@ function collectUndecoratedPages(items: IUndecoratedMenuItem[], undecoratedPages
88
93
 
89
94
  export const MAX_INT = 1000000000000; // 9007199254740991
90
95
 
96
+ function makeCategoryMenuItem_(item: IUndecoratedMenuCategory, category: ICategory): IMenuCategory | IMenuGroup {
97
+ const id = item.id || uuidv4();
98
+ const title = item.title || category.title || '';
99
+ const abstract = item.abstract || undefined;
100
+ const customClass = item.customClass || undefined;
101
+ const media = item.media || category.media;
102
+ const items: IMenuItem[] = [];
103
+ return category.isHidden ?
104
+ {
105
+ type: 'group',
106
+ id,
107
+ title,
108
+ abstract,
109
+ media,
110
+ customClass,
111
+ items,
112
+ } : {
113
+ type: 'category',
114
+ category: category.id,
115
+ id,
116
+ href: '',
117
+ title,
118
+ abstract,
119
+ media,
120
+ customClass,
121
+ items,
122
+ };
123
+ }
124
+
91
125
  function decorateMenuCategory_(
92
126
  item: IUndecoratedMenuCategory,
93
127
  categories: ICategory[],
94
128
  routes: IRoute[],
95
129
  market: string,
96
130
  depth: number = 0
131
+ ): IMenuItem | undefined {
132
+ const strategy = item.strategy || MenuCategoryStrategy.Default;
133
+ const categoryId = asCategoryId(item.category);
134
+ // find category
135
+ const category = categoryId ? categories.find(x => x.id === categoryId) : undefined;
136
+ if (category) {
137
+ const decoratedItem = makeCategoryMenuItem_(item, category);
138
+ if (!category.isHidden) {
139
+ const route = routes.find((route: IRoute) => route.isDefault && route.resolvedCategory === category.id);
140
+ if (route) {
141
+ (decoratedItem as IMenuCategory).href = route.id;
142
+ }
143
+ }
144
+ // checking for max depth recursion
145
+ let maxDepth = item.maxDepth;
146
+ if (typeof maxDepth === 'number') {
147
+ if (depth > maxDepth) {
148
+ return decoratedItem;
149
+ }
150
+ maxDepth--;
151
+ }
152
+ // search child categories
153
+ const childCategories = (category.id ?
154
+ categories.filter(x => asCategoryId(x.category) === category.id) : []
155
+ ).filter(x => {
156
+ switch (strategy) {
157
+ case MenuCategoryStrategy.ExcludeHidden:
158
+ return Boolean(x.isHidden) === false;
159
+ case MenuCategoryStrategy.Default:
160
+ return true;
161
+ }
162
+ });
163
+ // sort child categories
164
+ childCategories.sort((a, b) => {
165
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
166
+ });
167
+ // define sub strategy (!!! todo)
168
+ const subStrategy = strategy;
169
+ // decorate child categories
170
+ const childCategoryItems = childCategories.map(c => decorateMenuCategory_({
171
+ id: c.id,
172
+ type: 'category',
173
+ category: c.id,
174
+ maxDepth,
175
+ strategy: subStrategy, // dispatch sub strategy
176
+ items: [],
177
+ } as IUndecoratedMenuCategory, categories, routes, market, depth + 1));
178
+ // find child routes
179
+ const childRoutes = routes.filter((route: IRoute) => {
180
+ switch (strategy) {
181
+ case MenuCategoryStrategy.ExcludeHidden:
182
+ return route.resolvedCategory === category.id && Boolean(route.isDefault) === false;
183
+ case MenuCategoryStrategy.Default:
184
+ return route.category === category.id && (category.isHidden || Boolean(route.isDefault) === false);
185
+ }
186
+ });
187
+ // sort child routes
188
+ childRoutes.sort((a, b) => {
189
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
190
+ });
191
+ // decorate child routes
192
+ const childRouteItems: IMenuItem[] = childRoutes.map(x => decorateMenuRoute_(x));
193
+ // filter items
194
+ decoratedItem.items = [
195
+ ...childCategoryItems,
196
+ ...childRouteItems,
197
+ ].filter(x => Boolean(x)) as IMenuItem[];
198
+ return decoratedItem;
199
+ }
200
+ // return unresolved category as group
201
+ return {
202
+ id: item.id || uuidv4(),
203
+ type: 'group',
204
+ title: item.title || '',
205
+ items: [],
206
+ };
207
+ }
208
+
209
+ function decorateMenuCategoryWithHiddenSubCategories_(
210
+ item: IUndecoratedMenuCategory,
211
+ categories: ICategory[],
212
+ routes: IRoute[],
213
+ market: string,
214
+ depth: number = 0
97
215
  ): IMenuItem {
98
216
  const decoratedItem: IMenuCategory = {
99
217
  abstract: item.abstract,
100
218
  category: '',
101
219
  customClass: item.customClass,
102
220
  href: '',
103
- id: item.id || '',
221
+ id: item.id || uuidv4(),
104
222
  items: [],
105
223
  title: '',
106
224
  type: item.type,
107
225
  };
108
- const category = (typeof item.category === 'string' ? categories.find(x => x.id === item.category) : item.category) as ICategory;
226
+ /*
227
+ const categoryId = asCategoryId(item.category);
228
+ const category = categoryId ? categories.find(x => x.id === categoryId) : undefined;
229
+ */
230
+ const category = findCategoryInCategories(categories, item.category);
109
231
  if (category) {
110
232
  // assigning category id
111
233
  decoratedItem.category = category.id;
112
234
  // assigning category title
113
235
  decoratedItem.title = item.title || category.title || '';
114
236
  // assigning current or category media
115
- decoratedItem.media = decoratedItem.media || category.media;
237
+ decoratedItem.media = item.media || category.media;
116
238
  // find category's index page route
117
- const route = routes.find(x => x.isDefault && x.category === category.id);
239
+ const route = findRouteOfCategory(routes, category.id);
118
240
  if (route) {
119
241
  // hasLink
120
242
  decoratedItem.href = route.id;
@@ -130,32 +252,69 @@ function decorateMenuCategory_(
130
252
  maxDepth--;
131
253
  }
132
254
  // search child categories
133
- const childCategories = categories.filter(x => (
134
- x.category && (typeof x.category === 'object' ?
135
- x.category.id === category.id :
136
- x.category === category.id)
137
- ));
255
+ const childCategories = findChildCategories(categories, category.id);
138
256
  if (childCategories.length) {
139
257
  // sort child categories
140
258
  childCategories.sort((a, b) => {
141
259
  return (a.order || MAX_INT) - (b.order || MAX_INT);
142
260
  });
143
- decoratedItem.items = childCategories.map(c => decorateMenuCategory_({
144
- id: c.id,
145
- type: 'category',
146
- category: c.id,
147
- maxDepth,
148
- items: [],
149
- } as IUndecoratedMenuCategory, categories, routes, market, depth + 1));
150
- } else {
151
- // find child routes
152
- const childRoutes = routes.filter(x => !x.isDefault && x.category === category.id);
153
- // sort child routes
154
- childRoutes.sort((a, b) => {
155
- return (a.order || MAX_INT) - (b.order || MAX_INT);
261
+ decoratedItem.items = childCategories.map(c => {
262
+ const menuCategory = decorateMenuCategoryWithHiddenSubCategories_({
263
+ id: c.id,
264
+ type: 'category',
265
+ category: c.id,
266
+ maxDepth,
267
+ items: [],
268
+ } as IUndecoratedMenuCategory, categories, routes, market, depth + 1);
269
+ if (c.isHidden) {
270
+ const group: IMenuGroup = {
271
+ id: c.id,
272
+ type: 'group',
273
+ title: c.title,
274
+ items: [
275
+ ...(menuCategory.items || []),
276
+ ],
277
+ };
278
+ // find child routes
279
+ // const childRoutes = findRoutesOfCategory(routes, c.id, false);
280
+ const childRoutes = routes.filter((route: IRoute) =>
281
+ route.category === c.id &&
282
+ Boolean(route.isDefault) === false
283
+ );
284
+ // sort child routes
285
+ childRoutes.sort((a, b) => {
286
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
287
+ });
288
+ const routeLinks = childRoutes.map(x => decorateMenuRoute_(x));
289
+ group.items?.push(...routeLinks);
290
+ return group;
291
+ } else {
292
+ return decorateMenuCategoryWithHiddenSubCategories_({
293
+ id: c.id,
294
+ type: 'category',
295
+ category: c.id,
296
+ maxDepth,
297
+ items: [],
298
+ } as IUndecoratedMenuCategory, categories, routes, market, depth + 1);
299
+ }
156
300
  });
157
- decoratedItem.items = childRoutes.map(x => decorateMenuRoute_(x));
158
301
  }
302
+ // find child routes
303
+ // const childRoutes = findRoutesOfCategory(routes, category.id, false);
304
+ const childRoutes = routes.filter((route: IRoute) =>
305
+ route.category === category.id &&
306
+ Boolean(route.isDefault) === false
307
+ );
308
+ // console.log('childRoutes', childRoutes, category.id);
309
+ // sort child routes
310
+ childRoutes.sort((a, b) => {
311
+ return (a.order || MAX_INT) - (b.order || MAX_INT);
312
+ });
313
+ decoratedItem.items = [
314
+ ...(decoratedItem.items || []),
315
+ ...childRoutes.map(x => decorateMenuRoute_(x)),
316
+ ];
317
+
159
318
  }
160
319
  return decoratedItem;
161
320
  }
@@ -164,7 +323,7 @@ function decorateMenuRoute_(
164
323
  item: IRoute
165
324
  ): IMenuItem {
166
325
  const decoratedItem: IMenuRoute = {
167
- id: item.page,
326
+ id: uuidv4(),
168
327
  type: 'route',
169
328
  title: item.title,
170
329
  href: item.id,
@@ -189,7 +348,7 @@ async function decorateMenuPage_(
189
348
  );
190
349
  const page = await store[item.page.relationTo].findOne<ICategorized>(id, { ...params });
191
350
  const decoratedItem: IMenuPage = {
192
- id: item.id || '',
351
+ id: item.id || uuidv4(),
193
352
  type: item.type,
194
353
  title: item.title || page?.title || '',
195
354
  abstract: item.abstract || page?.abstract || '',