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