@mattisvensson/strapi-plugin-webatlas 0.9.6 → 0.10.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 (81) hide show
  1. package/README.md +24 -36
  2. package/dist/_chunks/{SettingTitle-77mvMRg_.mjs → SettingTitle-CdR3SVn_.mjs} +1 -1
  3. package/dist/_chunks/{SettingTitle-DLj_Wwqy.js → SettingTitle-RU1azFIM.js} +1 -1
  4. package/dist/_chunks/{de-C-uxto84.mjs → de-B5pRvs13.mjs} +13 -7
  5. package/dist/_chunks/{de-CGXL_3o_.js → de-CqU1FU8C.js} +13 -7
  6. package/dist/_chunks/{en-B1CHnIH7.mjs → en-BE-zzIv8.mjs} +13 -7
  7. package/dist/_chunks/{en-DWEd5BXK.js → en-C7I90FwV.js} +13 -7
  8. package/dist/_chunks/{index-DwaKIwAz.mjs → index-B07UVUOa.mjs} +387 -229
  9. package/dist/_chunks/{index-3pYLMhui.mjs → index-BmyxSosC.mjs} +3 -3
  10. package/dist/_chunks/{index-Dt-AXdaw.js → index-BucL4va6.js} +38 -82
  11. package/dist/_chunks/{index-BRKi-K-v.mjs → index-BvcX9hcc.mjs} +61 -24
  12. package/dist/_chunks/{index-COfk3YSm.js → index-BxpDM360.js} +386 -228
  13. package/dist/_chunks/{index-5OG4i6qO.mjs → index-CIM-JzLK.mjs} +38 -82
  14. package/dist/_chunks/{index-BWzalvVi.mjs → index-CNKWb8pn.mjs} +614 -320
  15. package/dist/_chunks/{index-VXuAEnpX.js → index-D-vJE_K8.js} +3 -3
  16. package/dist/_chunks/{index-BOtvXSPU.js → index-IRSCe8PX.js} +609 -315
  17. package/dist/_chunks/{index-DTsHvlTa.js → index-d09V61nm.js} +61 -24
  18. package/dist/admin/index.js +1 -1
  19. package/dist/admin/index.mjs +1 -1
  20. package/dist/admin/src/components/CMEditViewAside/NewPathInfo.d.ts +2 -0
  21. package/dist/admin/src/components/CMEditViewAside/OverrideCheckbox.d.ts +7 -0
  22. package/dist/admin/src/components/CMEditViewAside/Panel.d.ts +5 -0
  23. package/dist/admin/src/components/CMEditViewAside/PathInput.d.ts +11 -0
  24. package/dist/admin/src/components/CMEditViewAside/RouteStructure.d.ts +3 -0
  25. package/dist/admin/src/components/CMEditViewAside/UidPathDisplay.d.ts +4 -0
  26. package/dist/admin/src/components/PathInfo.d.ts +2 -3
  27. package/dist/admin/src/components/modals/externalItem/index.d.ts +1 -1
  28. package/dist/admin/src/components/modals/internalItem/ItemDetails.d.ts +13 -0
  29. package/dist/admin/src/components/modals/internalItem/internalItemCreate.d.ts +1 -1
  30. package/dist/admin/src/components/modals/internalItem/internalItemEdit.d.ts +1 -1
  31. package/dist/admin/src/components/modals/useModalSharedLogic.d.ts +1 -1
  32. package/dist/admin/src/components/modals/wrapperItem/index.d.ts +1 -1
  33. package/dist/admin/src/hooks/useApi.d.ts +4 -3
  34. package/dist/admin/src/pages/Navigation/RouteItem.d.ts +1 -15
  35. package/dist/admin/src/pages/Navigation/RouteItemBadge.d.ts +4 -0
  36. package/dist/admin/src/pages/Navigation/RouteItemIcon.d.ts +5 -0
  37. package/dist/admin/src/pages/Navigation/RouteItemMenu.d.ts +10 -0
  38. package/dist/admin/src/pages/Navigation/RouteItemStatus.d.ts +5 -0
  39. package/dist/admin/src/pages/Navigation/SortableRouteItem.d.ts +1 -1
  40. package/dist/admin/src/types/index.d.ts +3 -0
  41. package/dist/admin/src/types/modal.d.ts +56 -0
  42. package/dist/admin/src/types/navigation.d.ts +18 -0
  43. package/dist/admin/src/types/panel.d.ts +41 -0
  44. package/dist/admin/src/types/route.d.ts +1 -1
  45. package/dist/admin/src/utils/buildBreadcrumbString.d.ts +16 -0
  46. package/dist/admin/src/utils/createTempNavItemObject.d.ts +6 -8
  47. package/dist/admin/src/utils/duplicateCheck.d.ts +10 -4
  48. package/dist/admin/src/utils/findParentNavItem.d.ts +13 -0
  49. package/dist/admin/src/utils/index.d.ts +3 -2
  50. package/dist/server/index.js +630 -200
  51. package/dist/server/index.mjs +630 -200
  52. package/dist/server/src/content-types/index.d.ts +18 -0
  53. package/dist/server/src/content-types/route/index.d.ts +18 -0
  54. package/dist/server/src/content-types/route/schema.d.ts +18 -0
  55. package/dist/server/src/controllers/admin.d.ts +3 -2
  56. package/dist/server/src/controllers/index.d.ts +3 -2
  57. package/dist/server/src/index.d.ts +24 -4
  58. package/dist/server/src/migrations/001-canonical-path.d.ts +7 -0
  59. package/dist/server/src/migrations/index.d.ts +3 -0
  60. package/dist/server/src/services/admin.d.ts +3 -2
  61. package/dist/server/src/services/index.d.ts +3 -2
  62. package/dist/server/src/utils/buildCanonicalPath.d.ts +1 -0
  63. package/dist/server/src/utils/buildNavigationPath.d.ts +5 -0
  64. package/dist/server/src/utils/cascadeCanonicalPathUpdates.d.ts +1 -0
  65. package/dist/server/src/utils/getNonInternalRouteIds.d.ts +1 -0
  66. package/dist/server/src/utils/getRouteAncestors.d.ts +1 -0
  67. package/dist/server/src/utils/getRouteDescendants.d.ts +1 -0
  68. package/dist/server/src/utils/index.d.ts +10 -2
  69. package/dist/server/src/utils/navigationItemStructure.d.ts +27 -0
  70. package/dist/server/src/utils/routeHandler.d.ts +4 -2
  71. package/dist/server/src/utils/validateRouteDependencies.d.ts +4 -0
  72. package/dist/types/index.d.ts +0 -1
  73. package/dist/types/navigation.d.ts +13 -12
  74. package/dist/types/route.d.ts +7 -2
  75. package/dist/types/strapi.d.ts +1 -2
  76. package/dist/utils/index.d.ts +1 -2
  77. package/package.json +1 -1
  78. package/dist/admin/src/components/CMEditViewAside/Path.d.ts +0 -5
  79. package/dist/admin/src/utils/countChildren.d.ts +0 -2
  80. package/dist/types/modal.d.ts +0 -36
  81. package/dist/utils/getPath.d.ts +0 -1
@@ -1,10 +1,4 @@
1
1
  "use strict";
2
- function getPath(parentPath, slug) {
3
- if (!slug) return null;
4
- if (!parentPath) return slug;
5
- const newPath = parentPath.endsWith("/") ? parentPath : `${parentPath}/`;
6
- return `${newPath}${slug}`;
7
- }
8
2
  function transformToUrl(input) {
9
3
  const specialCharMap = {
10
4
  "ü": "ue",
@@ -16,6 +10,7 @@ function transformToUrl(input) {
16
10
  input = input.replace(/\/+/g, "/");
17
11
  input = input.startsWith("/") ? input.slice(1) : input;
18
12
  input = input.endsWith("/") ? input.slice(0, -1) : input;
13
+ input = input.replace(/\//g, "-");
19
14
  for (const char in specialCharMap) {
20
15
  const regex = new RegExp(char, "g");
21
16
  input = input.replace(regex, specialCharMap[char]);
@@ -26,7 +21,7 @@ function transformToUrl(input) {
26
21
  input = input.replace(/-+/g, "-");
27
22
  return input;
28
23
  }
29
- const version = "0.9.5";
24
+ const version = "0.9.6";
30
25
  const keywords = [];
31
26
  const type = "commonjs";
32
27
  const exports$1 = {
@@ -151,15 +146,10 @@ async function checkPathExists(path, targetRoutePath) {
151
146
  const entities = await strapi.documents(waRoute).findMany({
152
147
  filters: {
153
148
  $or: [
154
- {
155
- path
156
- },
157
- {
158
- slug: path
159
- },
160
- {
161
- uidPath: path
162
- }
149
+ { path },
150
+ { slug: path },
151
+ { uidPath: path },
152
+ { canonicalPath: path }
163
153
  ]
164
154
  }
165
155
  });
@@ -186,27 +176,12 @@ async function duplicateCheck(initialPath, targetRouteDocumentId) {
186
176
  }
187
177
  return uniquePath;
188
178
  } catch (e) {
189
- console.log(e);
179
+ strapi.log.error(e);
190
180
  }
191
181
  }
192
182
  async function createNavItem(data) {
193
183
  try {
194
184
  if (!data.route || !data.navigation) return null;
195
- const parent = data.parent ? await strapi.documents(waNavItem).findOne({
196
- documentId: data.parent,
197
- populate: ["route"]
198
- }) : null;
199
- const route2 = data.route ? await strapi.documents(waRoute).findOne({
200
- documentId: data.route
201
- }) : null;
202
- let path = route2.slug;
203
- if (route2.type === "internal" && !route2.isOverride && parent?.route.type === "internal") path = getPath(parent?.route?.path, route2.slug);
204
- await strapi.documents(waRoute).update({
205
- documentId: data.route,
206
- data: {
207
- path
208
- }
209
- });
210
185
  const entity = await strapi.documents(waNavItem).create({
211
186
  data: {
212
187
  navigation: data.navigation,
@@ -217,7 +192,7 @@ async function createNavItem(data) {
217
192
  });
218
193
  return entity;
219
194
  } catch (e) {
220
- console.log(e);
195
+ strapi.log.error(e);
221
196
  }
222
197
  }
223
198
  async function updateNavItem(documentId, data) {
@@ -230,7 +205,7 @@ async function updateNavItem(documentId, data) {
230
205
  data: updateData
231
206
  });
232
207
  } catch (e) {
233
- console.log(e);
208
+ strapi.log.error(e);
234
209
  }
235
210
  }
236
211
  async function deleteNavItem(documentId) {
@@ -240,7 +215,7 @@ async function deleteNavItem(documentId) {
240
215
  });
241
216
  return true;
242
217
  } catch (e) {
243
- console.log(e);
218
+ strapi.log.error(e);
244
219
  }
245
220
  }
246
221
  function getAdminService() {
@@ -282,7 +257,31 @@ async function createExternalRoute(data) {
282
257
  }
283
258
  });
284
259
  } catch (e) {
285
- console.log(e);
260
+ strapi.log.error(e);
261
+ }
262
+ }
263
+ async function updateRoute(documentId, data) {
264
+ try {
265
+ const entity = await strapi.documents(waRoute).update({
266
+ documentId,
267
+ data: {
268
+ ...data
269
+ }
270
+ });
271
+ return entity;
272
+ } catch (e) {
273
+ strapi.log.error(e);
274
+ }
275
+ }
276
+ async function deleteRoute(documentId) {
277
+ try {
278
+ await strapi.documents(waRoute).delete({
279
+ documentId
280
+ });
281
+ return true;
282
+ } catch (e) {
283
+ strapi.log.error(e);
284
+ return false;
286
285
  }
287
286
  }
288
287
  function buildStructuredNavigation(navigation2, variant = "nested") {
@@ -336,7 +335,7 @@ function buildStructuredNavigation(navigation2, variant = "nested") {
336
335
  return { ...navigation2, items: flattenedItems };
337
336
  }
338
337
  } catch (error) {
339
- console.error(error);
338
+ strapi.log.error(error);
340
339
  throw error;
341
340
  }
342
341
  }
@@ -377,8 +376,6 @@ function extractRouteAndItems(items) {
377
376
  delete route2.createdAt;
378
377
  delete route2.updatedAt;
379
378
  delete route2.isOverride;
380
- delete route2.internal;
381
- delete route2.wrapper;
382
379
  return {
383
380
  __component: route2.type === "wrapper" ? `${PLUGIN_ID}.wrapper` : `${PLUGIN_ID}.route`,
384
381
  type: route2.type,
@@ -4399,10 +4396,406 @@ function cleanRootKeys(obj) {
4399
4396
  function removeWaFields(obj) {
4400
4397
  delete obj["webatlas_path"];
4401
4398
  delete obj["webatlas_override"];
4399
+ delete obj["webatlas_parent"];
4402
4400
  return obj;
4403
4401
  }
4402
+ async function buildCanonicalPath(slug, parentDocumentId) {
4403
+ try {
4404
+ const parentRoute = await strapi.documents(waRoute).findOne({
4405
+ documentId: parentDocumentId
4406
+ });
4407
+ const parentCanonicalPath = parentRoute?.canonicalPath || "";
4408
+ const canonicalPath = `${parentCanonicalPath ? parentCanonicalPath + "/" : ""}${slug}`;
4409
+ return canonicalPath;
4410
+ } catch (err) {
4411
+ strapi.log.error("Error building canonical path:", err);
4412
+ return slug;
4413
+ }
4414
+ }
4415
+ async function cascadeCanonicalPathUpdates(parentRouteId, newParentCanonicalPath) {
4416
+ const children = await strapi.db.query(waRoute).findMany({
4417
+ where: {
4418
+ parent: {
4419
+ documentId: parentRouteId
4420
+ }
4421
+ }
4422
+ });
4423
+ for (const child of children) {
4424
+ const newCanonicalPath = `${newParentCanonicalPath}/${child.slug}`;
4425
+ const updateData = {
4426
+ canonicalPath: newCanonicalPath,
4427
+ // Only update path if not manually overridden
4428
+ ...child.isOverride ? {} : { path: newCanonicalPath }
4429
+ };
4430
+ await strapi.documents(waRoute).update({
4431
+ documentId: child.documentId,
4432
+ data: updateData
4433
+ });
4434
+ await cascadeCanonicalPathUpdates(child.documentId, newCanonicalPath);
4435
+ }
4436
+ }
4437
+ async function getRouteDescendants(routeId) {
4438
+ const descendants = [];
4439
+ const stack = [routeId];
4440
+ while (stack.length > 0) {
4441
+ const currentId = stack.pop();
4442
+ if (!currentId) continue;
4443
+ const children = await strapi.documents(waRoute).findMany({
4444
+ filters: {
4445
+ parent: {
4446
+ documentId: currentId
4447
+ }
4448
+ }
4449
+ });
4450
+ for (const child of children) {
4451
+ descendants.push(child.documentId);
4452
+ stack.push(child.documentId);
4453
+ }
4454
+ }
4455
+ return descendants;
4456
+ }
4457
+ async function getNonInternalRouteIds() {
4458
+ const routes2 = await strapi.documents(waRoute).findMany({
4459
+ filters: {
4460
+ type: {
4461
+ $ne: "internal"
4462
+ }
4463
+ }
4464
+ });
4465
+ const routeIds = routes2.map((route2) => route2.documentId);
4466
+ return routeIds;
4467
+ }
4468
+ async function validateRouteDependencies({
4469
+ routeId,
4470
+ newParentId
4471
+ }) {
4472
+ if (!newParentId) return true;
4473
+ const normalizedRouteId = routeId ?? void 0;
4474
+ const parentRoute = await strapi.documents(waRoute).findOne({
4475
+ documentId: newParentId
4476
+ });
4477
+ if (!parentRoute) {
4478
+ throw new Error(`Parent route not found: ${newParentId}`);
4479
+ }
4480
+ if (parentRoute?.type === "external") {
4481
+ throw new Error("External routes cannot have children");
4482
+ }
4483
+ if (!normalizedRouteId) return true;
4484
+ const descendants = await getRouteDescendants(normalizedRouteId);
4485
+ const nonInternalRouteIds = await getNonInternalRouteIds();
4486
+ if (normalizedRouteId === newParentId || descendants.includes(newParentId) || nonInternalRouteIds.includes(newParentId)) {
4487
+ throw new Error(`Circular dependency detected: Cannot set route ${newParentId} as parent of ${normalizedRouteId}`);
4488
+ }
4489
+ return true;
4490
+ }
4491
+ async function buildNavigationPath({
4492
+ slug,
4493
+ routeDocumentId,
4494
+ calculatedParent
4495
+ }) {
4496
+ let parentDocumentId = calculatedParent;
4497
+ let parent = null;
4498
+ if (parentDocumentId) {
4499
+ do {
4500
+ const navItem = await strapi.documents(waNavItem).findOne({
4501
+ documentId: parentDocumentId,
4502
+ populate: ["route", "parent"]
4503
+ });
4504
+ parent = navItem;
4505
+ parentDocumentId = navItem?.parent?.documentId || null;
4506
+ if (parent?.route?.type === "internal") break;
4507
+ } while (parentDocumentId);
4508
+ }
4509
+ if (parent?.route?.type !== "internal")
4510
+ parent = null;
4511
+ if (slug.startsWith("/")) slug = slug.substring(1);
4512
+ const newPath = parent?.route ? `${parent.route.path}/${slug}` : `${slug}`;
4513
+ const validatedPath = await duplicateCheck(newPath, routeDocumentId);
4514
+ return validatedPath;
4515
+ }
4516
+ async function handleItemDeletion(navigationItems) {
4517
+ const errors = [];
4518
+ let items = [...navigationItems];
4519
+ for (let i = 0; i < items.length; i++) {
4520
+ const item = items[i];
4521
+ if (item.clientModifications?.type === "delete") {
4522
+ try {
4523
+ if (item.documentId) {
4524
+ await deleteNavItem(item.documentId);
4525
+ }
4526
+ if (item.route.type !== "internal") {
4527
+ await deleteRoute(item.route.documentId);
4528
+ }
4529
+ const newItems = reduceDepthOfOrphanedItems(items, item.documentId);
4530
+ if (!newItems) {
4531
+ throw new Error("Failed to reduce depth of orphaned items");
4532
+ }
4533
+ items = newItems;
4534
+ i--;
4535
+ } catch (err) {
4536
+ const errorMsg = `Error deleting navigation item: ${err instanceof Error ? err.message : String(err)}`;
4537
+ errors.push(errorMsg);
4538
+ strapi.log.error(errorMsg, err);
4539
+ }
4540
+ continue;
4541
+ }
4542
+ if (!item.route && item.documentId) {
4543
+ try {
4544
+ strapi.log.warn("Navigation item without route found. Deleting it.", item);
4545
+ await deleteNavItem(item.documentId);
4546
+ items.splice(i, 1);
4547
+ i--;
4548
+ } catch (err) {
4549
+ const errorMsg = `Error deleting navigation item without route: ${err instanceof Error ? err.message : String(err)}`;
4550
+ errors.push(errorMsg);
4551
+ strapi.log.error(errorMsg, err);
4552
+ }
4553
+ continue;
4554
+ }
4555
+ }
4556
+ return {
4557
+ success: errors.length === 0,
4558
+ items,
4559
+ errors
4560
+ };
4561
+ }
4562
+ async function handleItemUpdate({
4563
+ item,
4564
+ calculatedParent,
4565
+ calculatedOrder,
4566
+ navigationId,
4567
+ newNavItemsMap
4568
+ }) {
4569
+ const errors = [];
4570
+ const isCreate = item.clientModifications?.type === "create";
4571
+ const isUpdate = item.clientModifications?.type === "update";
4572
+ const isInternal = item.route.type === "internal";
4573
+ if (isCreate && !item.clientModifications.route) {
4574
+ try {
4575
+ const newRoute = await createExternalRoute({
4576
+ title: item.route.title,
4577
+ slug: item.route.slug,
4578
+ path: item.route.path,
4579
+ type: item.route.type
4580
+ });
4581
+ const newNavItem = await createNavItem({
4582
+ route: newRoute.documentId,
4583
+ navigation: navigationId,
4584
+ parent: calculatedParent,
4585
+ order: calculatedOrder
4586
+ });
4587
+ if (newNavItem) newNavItemsMap.set(item.documentId, newNavItem);
4588
+ } catch (err) {
4589
+ errors.push(err instanceof Error ? err.message : String(err));
4590
+ strapi.log.error(`Error creating navigation item '${item.route.title}': `, err);
4591
+ }
4592
+ return { success: errors.length === 0, errors };
4593
+ }
4594
+ if (isCreate && item.clientModifications.route) {
4595
+ try {
4596
+ const route2 = await strapi.documents(waRoute).findOne({
4597
+ documentId: item.clientModifications.route
4598
+ });
4599
+ if (!route2) throw new Error(`Related route not found for new navigation item '${item.route.title}'`);
4600
+ const path = await buildNavigationPath({
4601
+ slug: item.route.slug,
4602
+ routeDocumentId: route2.documentId,
4603
+ calculatedParent
4604
+ });
4605
+ await updateRoute(route2.documentId, {
4606
+ title: item.route.title,
4607
+ slug: item.route.slug,
4608
+ path,
4609
+ isOverride: path !== route2.canonicalPath
4610
+ });
4611
+ const newNavItem = await createNavItem({
4612
+ route: item.clientModifications.route,
4613
+ navigation: item.clientModifications.navigation,
4614
+ parent: calculatedParent,
4615
+ order: calculatedOrder
4616
+ });
4617
+ if (newNavItem) newNavItemsMap.set(item.documentId, newNavItem);
4618
+ } catch (err) {
4619
+ errors.push(err instanceof Error ? err.message : String(err));
4620
+ strapi.log.error(`Error creating navigation item with existing route '${item.route.title}': `, err);
4621
+ }
4622
+ return { success: errors.length === 0, errors };
4623
+ }
4624
+ const needsRouteUpdate = isUpdate || isInternal;
4625
+ if (needsRouteUpdate || isInternal) {
4626
+ try {
4627
+ const route2 = await strapi.documents(waRoute).findOne({
4628
+ documentId: item.route.documentId
4629
+ });
4630
+ if (!route2) throw new Error(`Related route not found for navigation item '${item.route.title}'`);
4631
+ const slug = item.clientModifications?.slug || item.route.slug;
4632
+ const path = isInternal ? await buildNavigationPath({ slug, routeDocumentId: route2.documentId, calculatedParent }) : slug;
4633
+ if (needsRouteUpdate) {
4634
+ await updateRoute(route2.documentId, {
4635
+ title: item.clientModifications?.title || item.route.title,
4636
+ slug,
4637
+ path,
4638
+ isOverride: path !== route2.canonicalPath
4639
+ });
4640
+ }
4641
+ if (isInternal) {
4642
+ await strapi.entityService.update(item.route.relatedContentType, item.route.relatedDocumentId, {
4643
+ data: {
4644
+ webatlas_path: path,
4645
+ webatlas_override: path !== route2.canonicalPath
4646
+ }
4647
+ });
4648
+ }
4649
+ } catch (err) {
4650
+ errors.push(err instanceof Error ? err.message : String(err));
4651
+ strapi.log.error(`Error processing route for navigation item '${item.route.title}': `, err);
4652
+ }
4653
+ }
4654
+ await updateNavItem(item.documentId, {
4655
+ parent: calculatedParent,
4656
+ order: calculatedOrder
4657
+ });
4658
+ return {
4659
+ success: errors.length === 0,
4660
+ errors
4661
+ };
4662
+ }
4663
+ function calculateParentAndOrder({
4664
+ navigationItems,
4665
+ item,
4666
+ index: index2,
4667
+ parentIds,
4668
+ groupIndices,
4669
+ newNavItemsMap
4670
+ }) {
4671
+ if (item.depth === 0) {
4672
+ if (groupIndices[0] !== void 0) {
4673
+ groupIndices[0] = groupIndices[0] + 1;
4674
+ } else {
4675
+ groupIndices[0] = 0;
4676
+ }
4677
+ parentIds.length = 0;
4678
+ } else {
4679
+ const previousItem = navigationItems[index2 - 1];
4680
+ if (previousItem && typeof previousItem.depth === "number") {
4681
+ if (item.depth === previousItem.depth + 1) {
4682
+ parentIds.push(previousItem.documentId.startsWith("temp-") ? newNavItemsMap.get(previousItem.documentId)?.documentId || previousItem.documentId : previousItem.documentId);
4683
+ groupIndices[item.depth] = 0;
4684
+ } else if (item.depth <= previousItem.depth) {
4685
+ const diff = previousItem.depth - item.depth;
4686
+ for (let i = 0; i < diff; i++) {
4687
+ parentIds.pop();
4688
+ groupIndices.pop();
4689
+ }
4690
+ groupIndices[item.depth] = (groupIndices[item.depth] || 0) + 1;
4691
+ } else {
4692
+ groupIndices[item.depth] = (groupIndices[item.depth] || 0) + 1;
4693
+ }
4694
+ }
4695
+ }
4696
+ const calculatedParent = parentIds.at(-1) || null;
4697
+ const calculatedOrder = groupIndices[item.depth] || 0;
4698
+ return {
4699
+ calculatedParent,
4700
+ calculatedOrder
4701
+ };
4702
+ }
4703
+ const migration_001_canonical_path = {
4704
+ version: "001",
4705
+ description: "Migrate title field to canonicalPath using transformToUrl",
4706
+ async up(strapi2) {
4707
+ strapi2.log.info("[webatlas] Starting canonical path migration...");
4708
+ try {
4709
+ const routes2 = await strapi2.db?.query(waRoute).findMany({
4710
+ where: {
4711
+ title: {
4712
+ $notNull: true,
4713
+ $ne: ""
4714
+ },
4715
+ $or: [
4716
+ { canonicalPath: { $null: true } },
4717
+ { canonicalPath: "" }
4718
+ ]
4719
+ },
4720
+ populate: ["parent"]
4721
+ });
4722
+ if (!routes2 || routes2.length === 0) {
4723
+ strapi2.log.info("[webatlas] No routes found that need canonical path migration");
4724
+ return;
4725
+ }
4726
+ strapi2.log.info(`[webatlas] Found ${routes2.length} routes to migrate`);
4727
+ let migratedCount = 0;
4728
+ let errorCount = 0;
4729
+ const chunkSize = 50;
4730
+ for (let i = 0; i < routes2.length; i += chunkSize) {
4731
+ const chunk = routes2.slice(i, i + chunkSize);
4732
+ await Promise.all(chunk.map(async (route2) => {
4733
+ try {
4734
+ const transformedTitle = transformToUrl(route2.title);
4735
+ const parentId = route2.parent?.id || null;
4736
+ const canonicalPath = await buildCanonicalPath(transformedTitle, parentId);
4737
+ await strapi2.db?.query(waRoute).update({
4738
+ where: { id: route2.id },
4739
+ data: { canonicalPath }
4740
+ });
4741
+ migratedCount++;
4742
+ if (migratedCount % 25 === 0) {
4743
+ strapi2.log.info(`[webatlas] Migrated ${migratedCount}/${routes2.length} routes`);
4744
+ }
4745
+ } catch (error) {
4746
+ strapi2.log.error(`[webatlas] Failed to migrate route ${route2.id}:`, error);
4747
+ errorCount++;
4748
+ }
4749
+ }));
4750
+ }
4751
+ strapi2.log.info(`[webatlas] Canonical path migration completed. Migrated: ${migratedCount}, Errors: ${errorCount}`);
4752
+ if (errorCount > 0) {
4753
+ strapi2.log.warn(`[webatlas] ${errorCount} routes failed to migrate. Check logs for details.`);
4754
+ }
4755
+ } catch (error) {
4756
+ strapi2.log.error("[webatlas] Canonical path migration failed:", error);
4757
+ throw error;
4758
+ }
4759
+ }
4760
+ };
4761
+ const migrations = [
4762
+ migration_001_canonical_path
4763
+ ];
4764
+ const runMigrations = async (strapi2) => {
4765
+ const pluginStore = strapi2.store({ type: "plugin", name: PLUGIN_ID });
4766
+ let config2 = await pluginStore.get({ key: "config" });
4767
+ if (!config2) {
4768
+ config2 = { migrationVersion: "0" };
4769
+ }
4770
+ const currentVersion = config2.migrationVersion || "0";
4771
+ const pendingMigrations = migrations.filter((migration) => migration.version > currentVersion);
4772
+ if (pendingMigrations.length === 0) {
4773
+ strapi2.log.info("[webatlas] All migrations up to date");
4774
+ return;
4775
+ }
4776
+ strapi2.log.info(`[webatlas] Running ${pendingMigrations.length} migration(s)...`);
4777
+ for (const migration of pendingMigrations) {
4778
+ try {
4779
+ strapi2.log.info(`[webatlas] Running migration ${migration.version}: ${migration.description}`);
4780
+ await migration.up(strapi2);
4781
+ await pluginStore.set({
4782
+ key: "config",
4783
+ value: {
4784
+ ...config2,
4785
+ migrationVersion: migration.version
4786
+ }
4787
+ });
4788
+ strapi2.log.info(`[webatlas] Migration ${migration.version} completed successfully`);
4789
+ } catch (error) {
4790
+ strapi2.log.error(`[webatlas] Migration ${migration.version} failed:`, error);
4791
+ throw error;
4792
+ }
4793
+ }
4794
+ strapi2.log.info("[webatlas] All migrations completed successfully");
4795
+ };
4404
4796
  const bootstrap = async ({ strapi: strapi2 }) => {
4405
4797
  try {
4798
+ await runMigrations(strapi2);
4406
4799
  const actions = [
4407
4800
  {
4408
4801
  section: "plugins",
@@ -4458,15 +4851,15 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4458
4851
  navigation: {
4459
4852
  maxDepth: config2?.navigation?.maxDepth || 1,
4460
4853
  ...config2?.navigation
4461
- }
4854
+ },
4855
+ migrationVersion: config2?.migrationVersion || "0"
4462
4856
  };
4463
4857
  enabledContentTypes.forEach((type2) => {
4464
4858
  const existingConfig = config2?.selectedContentTypes?.find((ct) => ct.uid === type2.uid);
4465
4859
  newConfig.selectedContentTypes.push({
4466
4860
  uid: type2.uid,
4467
4861
  label: type2.info.displayName,
4468
- default: existingConfig?.default || null,
4469
- pattern: existingConfig?.pattern || null
4862
+ default: existingConfig?.default || null
4470
4863
  });
4471
4864
  });
4472
4865
  if (JSON.stringify(newConfig) !== JSON.stringify(config2)) {
@@ -4489,7 +4882,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4489
4882
  if (!navItem || !navItem.route) return;
4490
4883
  event.state = navItem.route.id && navItem.route.type === "external" ? { id: navItem.route.id } : null;
4491
4884
  } catch (err) {
4492
- console.log(err);
4885
+ strapi2.log.error(err);
4493
4886
  }
4494
4887
  },
4495
4888
  async afterDelete(event) {
@@ -4502,15 +4895,13 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4502
4895
  }
4503
4896
  });
4504
4897
  } catch (err) {
4505
- console.log(err);
4898
+ strapi2.log.error(err);
4506
4899
  }
4507
4900
  }
4508
4901
  });
4509
4902
  strapi2.db?.lifecycles.subscribe({
4510
4903
  models: enabledContentTypes.map((type2) => type2.uid),
4511
4904
  async beforeCreate(event) {
4512
- const validContentTypes = config2.selectedContentTypes.filter((type2) => strapi2.contentTypes[type2.uid]);
4513
- await pluginStore.set({ key: "config", value: { selectedContentTypes: validContentTypes } });
4514
4905
  if (!event.params.data.webatlas_path) return;
4515
4906
  event.params.data.webatlas_path = transformToUrl(event.params.data.webatlas_path);
4516
4907
  },
@@ -4518,7 +4909,8 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4518
4909
  const ctSettings = config2.selectedContentTypes.find((type2) => type2.uid === event.model.uid);
4519
4910
  const {
4520
4911
  webatlas_path,
4521
- webatlas_override
4912
+ webatlas_override,
4913
+ webatlas_parent
4522
4914
  } = event.params.data;
4523
4915
  if (!webatlas_path) return;
4524
4916
  const relatedRoute = await strapi2.db?.query(waRoute).findOne({
@@ -4527,8 +4919,20 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4527
4919
  }
4528
4920
  });
4529
4921
  if (relatedRoute) return;
4530
- const title = ctSettings?.default ? event.params.data[ctSettings.default] : "";
4922
+ let parent = null;
4923
+ if (webatlas_parent) {
4924
+ try {
4925
+ const isValid = await validateRouteDependencies({
4926
+ newParentId: webatlas_parent
4927
+ });
4928
+ if (isValid) parent = webatlas_parent;
4929
+ } catch (err) {
4930
+ strapi2.log.error(`Route dependency validation failed: ${err.message}`);
4931
+ }
4932
+ }
4531
4933
  const path = await duplicateCheck(transformToUrl(webatlas_path));
4934
+ const canonicalPath = await buildCanonicalPath(path, parent);
4935
+ const title = event.params.data[ctSettings?.default]?.trim() || path;
4532
4936
  await strapi2.documents(waRoute).create({
4533
4937
  data: {
4534
4938
  relatedContentType: event.model.uid,
@@ -4538,7 +4942,9 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4538
4942
  path,
4539
4943
  uidPath: `${event.model.singularName}/${event.result.id}`,
4540
4944
  isOverride: webatlas_override || false,
4541
- title
4945
+ title,
4946
+ parent,
4947
+ canonicalPath
4542
4948
  }
4543
4949
  });
4544
4950
  },
@@ -4547,46 +4953,74 @@ const bootstrap = async ({ strapi: strapi2 }) => {
4547
4953
  const {
4548
4954
  webatlas_path,
4549
4955
  webatlas_override,
4956
+ webatlas_parent,
4550
4957
  documentId
4551
4958
  } = event.params.data;
4552
4959
  if (!webatlas_path) return;
4553
- const relatedRoute = await strapi2.db?.query(waRoute).findOne({
4554
- where: {
4960
+ const relatedRoute = await strapi2.documents(waRoute).findFirst({
4961
+ filters: {
4555
4962
  relatedDocumentId: documentId
4556
4963
  }
4557
4964
  });
4558
- const title = ctSettings?.default ? event.params.data[ctSettings.default] : "";
4559
- const path = await duplicateCheck(transformToUrl(webatlas_path), relatedRoute ? relatedRoute.documentId : null);
4965
+ let parent = null;
4966
+ if (webatlas_parent) {
4967
+ try {
4968
+ const isValid = await validateRouteDependencies({
4969
+ routeId: relatedRoute ? relatedRoute.documentId : null,
4970
+ newParentId: webatlas_parent
4971
+ });
4972
+ if (isValid) {
4973
+ parent = await strapi2.documents(waRoute).findOne({
4974
+ documentId: webatlas_parent
4975
+ });
4976
+ }
4977
+ } catch (err) {
4978
+ strapi2.log.error(`Route dependency validation failed: ${err.message}`);
4979
+ }
4980
+ }
4981
+ const transformedPath = transformToUrl(webatlas_path);
4982
+ const rawPath = parent ? `${parent.path}/${transformedPath}` : transformedPath;
4983
+ const path = await duplicateCheck(rawPath, relatedRoute ? relatedRoute.documentId : null);
4984
+ const canonicalPath = await buildCanonicalPath(transformedPath, parent?.documentId);
4985
+ const title = event.params.data[ctSettings?.default]?.trim() || path;
4560
4986
  const routeData = {
4561
4987
  title,
4562
4988
  path,
4563
- slug: path,
4564
- isOverride: webatlas_override || false
4989
+ slug: transformedPath,
4990
+ isOverride: webatlas_override || false,
4991
+ parent: parent?.documentId || null
4565
4992
  };
4993
+ let routeDocumentId = relatedRoute?.documentId;
4566
4994
  if (!relatedRoute) {
4567
- await strapi2.documents(waRoute).create({
4995
+ const createdRoute = await strapi2.documents(waRoute).create({
4568
4996
  data: {
4569
4997
  relatedContentType: event.model.uid,
4570
4998
  relatedId: event.result.id,
4571
4999
  relatedDocumentId: event.result.documentId,
4572
5000
  uidPath: `${event.model.singularName}/${event.result.id}`,
5001
+ canonicalPath,
4573
5002
  ...routeData
4574
5003
  }
4575
5004
  });
5005
+ routeDocumentId = createdRoute?.documentId;
4576
5006
  } else {
4577
5007
  await strapi2.documents(waRoute).update({
4578
5008
  documentId: relatedRoute.documentId,
4579
5009
  data: {
4580
- ...routeData
5010
+ ...routeData,
5011
+ canonicalPath
4581
5012
  }
4582
5013
  });
4583
5014
  }
5015
+ if (routeDocumentId) {
5016
+ await cascadeCanonicalPathUpdates(routeDocumentId, canonicalPath);
5017
+ }
4584
5018
  },
4585
5019
  async afterDelete(event) {
4586
5020
  try {
4587
5021
  await findAndDeleteNavItem(event.result.id, event.model.uid);
4588
5022
  } catch (err) {
4589
- console.log(err);
5023
+ strapi2.log.error(err);
4590
5024
  }
4591
5025
  },
4592
5026
  async afterDeleteMany(event) {
@@ -4620,7 +5054,7 @@ async function findAndDeleteNavItem(relatedId, relatedContentType) {
4620
5054
  await strapi.documents(waRoute).delete({ documentId: route2.documentId });
4621
5055
  if (navItem?.documentId) await strapi.documents(waNavItem).delete({ documentId: navItem.documentId });
4622
5056
  } catch (err) {
4623
- console.log(err);
5057
+ strapi.log.error(err);
4624
5058
  }
4625
5059
  }
4626
5060
  const destroy = ({ strapi: strapi2 }) => {
@@ -5157,6 +5591,11 @@ const register = ({ strapi: strapi2 }) => {
5157
5591
  type: "boolean",
5158
5592
  private: true
5159
5593
  });
5594
+ set$1(attributes, "webatlas_parent", {
5595
+ ...fieldSettings,
5596
+ type: "string",
5597
+ private: true
5598
+ });
5160
5599
  });
5161
5600
  };
5162
5601
  const config = {
@@ -5215,6 +5654,10 @@ const schema$2 = {
5215
5654
  type: "string",
5216
5655
  configurable: false
5217
5656
  },
5657
+ canonicalPath: {
5658
+ type: "string",
5659
+ configurable: false
5660
+ },
5218
5661
  slug: {
5219
5662
  type: "string",
5220
5663
  configurable: false
@@ -5246,6 +5689,20 @@ const schema$2 = {
5246
5689
  default: "internal",
5247
5690
  configurable: false,
5248
5691
  required: true
5692
+ },
5693
+ parent: {
5694
+ type: "relation",
5695
+ relation: "manyToOne",
5696
+ target: "plugin::webatlas.route",
5697
+ inversedBy: "children",
5698
+ configurable: false
5699
+ },
5700
+ children: {
5701
+ type: "relation",
5702
+ relation: "oneToMany",
5703
+ target: "plugin::webatlas.route",
5704
+ mappedBy: "parent",
5705
+ configurable: false
5249
5706
  }
5250
5707
  }
5251
5708
  };
@@ -5374,19 +5831,18 @@ const admin$2 = () => ({
5374
5831
  return ctx.throw(500, e);
5375
5832
  }
5376
5833
  },
5377
- async getRoutes(ctx) {
5834
+ async getRoute(ctx) {
5378
5835
  try {
5379
- return await getAdminService().getRoutes();
5836
+ const { documentId } = ctx.params;
5837
+ if (!documentId) return ctx.throw(400, "Route documentId is required");
5838
+ return await getAdminService().getRoute(documentId);
5380
5839
  } catch (e) {
5381
5840
  return ctx.throw(500, e);
5382
5841
  }
5383
5842
  },
5384
- async updateRoute(ctx) {
5843
+ async getAllRoutes(ctx) {
5385
5844
  try {
5386
- const { documentId } = ctx.query;
5387
- if (!documentId) return ctx.throw(400, "Route documentId is required");
5388
- const { data } = ctx.request.body;
5389
- return await getAdminService().updateRoute(documentId, data);
5845
+ return await getAdminService().getAllRoutes();
5390
5846
  } catch (e) {
5391
5847
  return ctx.throw(500, e);
5392
5848
  }
@@ -5400,6 +5856,14 @@ const admin$2 = () => ({
5400
5856
  return ctx.throw(500, e);
5401
5857
  }
5402
5858
  },
5859
+ async getProhibitedRouteIds(ctx) {
5860
+ try {
5861
+ const { documentId } = ctx.params;
5862
+ return await getAdminService().getProhibitedRouteIds(documentId);
5863
+ } catch (e) {
5864
+ return ctx.throw(500, e);
5865
+ }
5866
+ },
5403
5867
  async getNavigation(ctx) {
5404
5868
  try {
5405
5869
  const { documentId, variant } = ctx.query;
@@ -5554,7 +6018,7 @@ const admin$1 = {
5554
6018
  {
5555
6019
  method: "GET",
5556
6020
  path: "/route",
5557
- handler: "admin.getRoutes",
6021
+ handler: "admin.getAllRoutes",
5558
6022
  config: {
5559
6023
  policies: [
5560
6024
  "admin::isAuthenticatedAdmin",
@@ -5571,9 +6035,9 @@ const admin$1 = {
5571
6035
  }
5572
6036
  },
5573
6037
  {
5574
- method: "PUT",
5575
- path: "/route",
5576
- handler: "admin.updateRoute",
6038
+ method: "GET",
6039
+ path: "/route/related",
6040
+ handler: "admin.getRelatedRoute",
5577
6041
  config: {
5578
6042
  policies: [
5579
6043
  "admin::isAuthenticatedAdmin",
@@ -5581,6 +6045,7 @@ const admin$1 = {
5581
6045
  name: `plugin::${PLUGIN_ID}.has-permissions`,
5582
6046
  config: {
5583
6047
  actions: [
6048
+ `plugin::${PLUGIN_ID}.cm.aside`,
5584
6049
  `plugin::${PLUGIN_ID}.page.navigation`
5585
6050
  ]
5586
6051
  }
@@ -5590,8 +6055,26 @@ const admin$1 = {
5590
6055
  },
5591
6056
  {
5592
6057
  method: "GET",
5593
- path: "/route/related",
5594
- handler: "admin.getRelatedRoute",
6058
+ path: "/route/prohibitedIds/:documentId?",
6059
+ handler: "admin.getProhibitedRouteIds",
6060
+ config: {
6061
+ policies: [
6062
+ "admin::isAuthenticatedAdmin",
6063
+ {
6064
+ name: `plugin::${PLUGIN_ID}.has-permissions`,
6065
+ config: {
6066
+ actions: [
6067
+ `plugin::${PLUGIN_ID}.cm.aside`
6068
+ ]
6069
+ }
6070
+ }
6071
+ ]
6072
+ }
6073
+ },
6074
+ {
6075
+ method: "GET",
6076
+ path: "/route/:documentId",
6077
+ handler: "admin.getRoute",
5595
6078
  config: {
5596
6079
  policies: [
5597
6080
  "admin::isAuthenticatedAdmin",
@@ -5599,7 +6082,6 @@ const admin$1 = {
5599
6082
  name: `plugin::${PLUGIN_ID}.has-permissions`,
5600
6083
  config: {
5601
6084
  actions: [
5602
- `plugin::${PLUGIN_ID}.cm.aside`,
5603
6085
  `plugin::${PLUGIN_ID}.page.navigation`
5604
6086
  ]
5605
6087
  }
@@ -5755,7 +6237,7 @@ const admin = ({ strapi: strapi2 }) => ({
5755
6237
  newConfigMerged = { ...config2, ...newConfig };
5756
6238
  await pluginStore.set({ key: "config", value: newConfigMerged });
5757
6239
  } catch (err) {
5758
- console.log(err);
6240
+ strapi2.log.error(err);
5759
6241
  return "Error. Couldn't update config";
5760
6242
  }
5761
6243
  return newConfigMerged;
@@ -5776,35 +6258,21 @@ const admin = ({ strapi: strapi2 }) => ({
5776
6258
  };
5777
6259
  return config2;
5778
6260
  },
5779
- async getRoutes() {
6261
+ async getRoute(documentId) {
5780
6262
  try {
5781
- const entities = await strapi2.documents(waRoute).findMany();
5782
- return entities;
6263
+ return await strapi2.documents(waRoute).findOne({
6264
+ documentId
6265
+ });
5783
6266
  } catch (e) {
5784
- console.log(e);
6267
+ strapi2.log.error(e);
5785
6268
  }
5786
6269
  },
5787
- // TODO: Types
5788
- async updateRoute(documentId, data) {
6270
+ async getAllRoutes() {
5789
6271
  try {
5790
- let checkedPath = data.path;
5791
- if (data.internal) {
5792
- const parent = data.parent ? await strapi2.documents(waNavItem).findOne({
5793
- documentId: data.parent
5794
- }) : null;
5795
- const path = data.isOverride ? data.slug : getPath(parent?.path, data.slug);
5796
- checkedPath = await duplicateCheck(path, documentId);
5797
- }
5798
- const entity = await strapi2.documents(waRoute).update({
5799
- documentId,
5800
- data: {
5801
- ...data,
5802
- path: checkedPath
5803
- }
5804
- });
5805
- return entity;
6272
+ const entities = await strapi2.documents(waRoute).findMany();
6273
+ return entities;
5806
6274
  } catch (e) {
5807
- console.log(e);
6275
+ strapi2.log.error(e);
5808
6276
  }
5809
6277
  },
5810
6278
  async getRelatedRoute(documentId) {
@@ -5812,10 +6280,28 @@ const admin = ({ strapi: strapi2 }) => ({
5812
6280
  return await strapi2.db?.query(waRoute).findOne({
5813
6281
  where: {
5814
6282
  relatedDocumentId: documentId
5815
- }
6283
+ },
6284
+ populate: ["parent"]
5816
6285
  });
5817
6286
  } catch (e) {
5818
- console.log(e);
6287
+ strapi2.log.error(e);
6288
+ }
6289
+ },
6290
+ async getProhibitedRouteIds(documentId) {
6291
+ try {
6292
+ let route2 = null;
6293
+ if (documentId) {
6294
+ route2 = await strapi2.documents(waRoute).findOne({
6295
+ documentId
6296
+ });
6297
+ }
6298
+ const descendants = route2?.documentId ? await getRouteDescendants(route2.documentId) : [];
6299
+ const nonInternalRouteIds = await getNonInternalRouteIds();
6300
+ const prohibitedRouteIds = [...descendants, ...nonInternalRouteIds];
6301
+ route2?.documentId && prohibitedRouteIds.push(route2.documentId);
6302
+ return prohibitedRouteIds;
6303
+ } catch (e) {
6304
+ strapi2.log.error(e);
5819
6305
  }
5820
6306
  },
5821
6307
  async getNavigation(documentId, variant) {
@@ -5851,7 +6337,7 @@ const admin = ({ strapi: strapi2 }) => ({
5851
6337
  }
5852
6338
  return navigation2;
5853
6339
  } catch (e) {
5854
- console.log(e);
6340
+ strapi2.log.error(e);
5855
6341
  }
5856
6342
  },
5857
6343
  async createNavigation(name2, visible) {
@@ -5864,7 +6350,7 @@ const admin = ({ strapi: strapi2 }) => ({
5864
6350
  }
5865
6351
  });
5866
6352
  } catch (e) {
5867
- console.log(e);
6353
+ strapi2.log.error(e);
5868
6354
  }
5869
6355
  },
5870
6356
  async updateNavigation(documentId, data) {
@@ -5878,7 +6364,7 @@ const admin = ({ strapi: strapi2 }) => ({
5878
6364
  });
5879
6365
  return entity;
5880
6366
  } catch (e) {
5881
- console.log(e);
6367
+ strapi2.log.error(e);
5882
6368
  }
5883
6369
  },
5884
6370
  async deleteNavigation(documentId) {
@@ -5897,115 +6383,46 @@ const admin = ({ strapi: strapi2 }) => ({
5897
6383
  documentId
5898
6384
  });
5899
6385
  } catch (e) {
5900
- console.log(e);
6386
+ strapi2.log.error(e);
5901
6387
  }
5902
6388
  },
5903
6389
  async updateNavigationItemStructure(navigationId, navigationItems) {
5904
6390
  if (!navigationId || !navigationItems) return;
5905
6391
  let error = false;
5906
- const newNavItemsMap = /* @__PURE__ */ new Map();
5907
- for (const [index2, item] of navigationItems.entries()) {
5908
- if (item.deleted) {
5909
- try {
5910
- item.documentId && await deleteNavItem(item.documentId);
5911
- const newItems = reduceDepthOfOrphanedItems(navigationItems, item.documentId);
5912
- if (!newItems) throw new Error("Failed to reduce depth of orphaned items");
5913
- navigationItems = newItems;
5914
- } catch (error2) {
5915
- error2 = true;
5916
- console.error("Error deleting navigation item ", error2);
5917
- }
5918
- continue;
5919
- }
5920
- if (!item.route && item.documentId) {
5921
- try {
5922
- console.warn("Navigation item without route found. Deleting it. ", item);
5923
- await deleteNavItem(item.documentId);
5924
- } catch (error2) {
5925
- console.error("Error deleting navigation item without route ", error2);
5926
- }
5927
- continue;
5928
- }
5929
- if (item.update && !item.isNew) {
5930
- try {
5931
- await this.updateRoute(item.route.documentId, {
5932
- title: item.update.title || item.route.title,
5933
- slug: item.update.slug || item.route.slug,
5934
- path: item.update.path || item.route.path,
5935
- isOverride: item.update.isOverride !== void 0 ? item.update.isOverride : item.route.isOverride
5936
- });
5937
- } catch (error2) {
5938
- error2 = true;
5939
- console.error("Error updating route ", error2);
5940
- }
5941
- }
6392
+ let newNavItemsMap = /* @__PURE__ */ new Map();
6393
+ const deletionResult = await handleItemDeletion(navigationItems);
6394
+ if (!deletionResult.success) {
6395
+ strapi2.log.error("Deletion errors:", deletionResult.errors);
5942
6396
  }
6397
+ navigationItems = deletionResult.items;
5943
6398
  let parentIds = [];
5944
6399
  let groupIndices = [];
5945
6400
  for (const [index2, item] of navigationItems.entries()) {
5946
6401
  if (typeof item.depth !== "number") {
5947
6402
  continue;
5948
6403
  }
5949
- if (item.depth === 0) {
5950
- if (groupIndices[0] !== void 0) {
5951
- groupIndices[0] = groupIndices[0] + 1;
5952
- } else {
5953
- groupIndices[0] = 0;
5954
- }
5955
- parentIds = [];
5956
- } else {
5957
- const previousItem = navigationItems[index2 - 1];
5958
- if (previousItem && typeof previousItem.depth === "number") {
5959
- if (item.depth === previousItem.depth + 1) {
5960
- parentIds.push(previousItem.documentId.startsWith("temp-") ? newNavItemsMap.get(previousItem.documentId)?.documentId || previousItem.documentId : previousItem.documentId);
5961
- groupIndices[item.depth] = 0;
5962
- } else if (item.depth <= previousItem.depth) {
5963
- const diff = previousItem.depth - item.depth;
5964
- for (let i = 0; i < diff; i++) {
5965
- parentIds.pop();
5966
- groupIndices.pop();
5967
- }
5968
- groupIndices[item.depth] = (groupIndices[item.depth] || 0) + 1;
5969
- } else {
5970
- groupIndices[item.depth] = (groupIndices[item.depth] || 0) + 1;
5971
- }
5972
- }
5973
- }
5974
- const calculatedParent = parentIds.at(-1) || null;
5975
- const calculatedOrder = groupIndices[item.depth] || 0;
5976
6404
  try {
5977
- if (item.isNew) {
5978
- if (item.isNew.route) {
5979
- await createNavItem({
5980
- route: item.isNew.route,
5981
- parent: calculatedParent,
5982
- navigation: item.isNew.navigation,
5983
- order: calculatedOrder
5984
- });
5985
- } else {
5986
- const newRoute = await createExternalRoute({
5987
- title: item.route.title,
5988
- slug: item.route.slug,
5989
- path: item.route.path,
5990
- type: item.route.type
5991
- });
5992
- const newNavItem = await createNavItem({
5993
- route: newRoute.documentId,
5994
- navigation: navigationId,
5995
- parent: calculatedParent,
5996
- order: calculatedOrder
5997
- });
5998
- if (newNavItem) newNavItemsMap.set(item.documentId, newNavItem);
5999
- }
6000
- } else {
6001
- await updateNavItem(item.documentId, {
6002
- order: calculatedOrder,
6003
- parent: calculatedParent
6004
- });
6405
+ const { calculatedParent, calculatedOrder } = calculateParentAndOrder({
6406
+ navigationItems,
6407
+ item,
6408
+ index: index2,
6409
+ parentIds,
6410
+ groupIndices,
6411
+ newNavItemsMap
6412
+ });
6413
+ const result = await handleItemUpdate({
6414
+ item,
6415
+ calculatedParent,
6416
+ calculatedOrder,
6417
+ navigationId,
6418
+ newNavItemsMap
6419
+ });
6420
+ if (!result.success) {
6421
+ strapi2.log.error("Error updating item: ", item);
6005
6422
  }
6006
6423
  } catch (errorMsg) {
6007
6424
  error = true;
6008
- console.error("Error updating navigation item ", errorMsg);
6425
+ strapi2.log.error("Error updating navigation item ", errorMsg);
6009
6426
  }
6010
6427
  }
6011
6428
  return !error;
@@ -6014,7 +6431,7 @@ const admin = ({ strapi: strapi2 }) => ({
6014
6431
  try {
6015
6432
  return await duplicateCheck(initialPath, targetRouteDocumentId);
6016
6433
  } catch (e) {
6017
- console.log(e);
6434
+ strapi2.log.error(e);
6018
6435
  }
6019
6436
  }
6020
6437
  });
@@ -6025,6 +6442,7 @@ const client = ({ strapi: strapi2 }) => ({
6025
6442
  filters: {
6026
6443
  $or: [
6027
6444
  { path: slug },
6445
+ { canonicalPath: slug },
6028
6446
  { uidPath: slug }
6029
6447
  ]
6030
6448
  }
@@ -6051,9 +6469,21 @@ const client = ({ strapi: strapi2 }) => ({
6051
6469
  if (!entity) return null;
6052
6470
  let cleanEntity = cleanRootKeys(entity);
6053
6471
  cleanEntity = removeWaFields(cleanEntity);
6054
- return { contentType: contentType.info.singularName, ...cleanEntity };
6472
+ const webatlasFields = {
6473
+ path: route2.path,
6474
+ canonicalPath: route2.canonicalPath,
6475
+ slug: route2.slug,
6476
+ uidPath: route2.uidPath
6477
+ };
6478
+ return {
6479
+ contentType: contentType.info.singularName,
6480
+ webatlas: {
6481
+ ...webatlasFields
6482
+ },
6483
+ ...cleanEntity
6484
+ };
6055
6485
  } catch (e) {
6056
- console.log(e);
6486
+ strapi2.log.error(e);
6057
6487
  return e;
6058
6488
  }
6059
6489
  },
@@ -6091,7 +6521,7 @@ const client = ({ strapi: strapi2 }) => ({
6091
6521
  try {
6092
6522
  navigation2 = await method.lookup();
6093
6523
  } catch (error) {
6094
- console.log(`Navigation lookup by ${method.name} failed:`, error);
6524
+ strapi2.log.error(`Navigation lookup by ${method.name} failed:`, error);
6095
6525
  }
6096
6526
  }
6097
6527
  }
@@ -6101,7 +6531,7 @@ const client = ({ strapi: strapi2 }) => ({
6101
6531
  const entityNavigation = extractRouteAndItems(structured.items);
6102
6532
  return { ...structured, items: entityNavigation };
6103
6533
  } catch (e) {
6104
- console.log(e);
6534
+ strapi2.log.error(e);
6105
6535
  return e;
6106
6536
  }
6107
6537
  }