@vendure/dashboard 3.4.2-master-202509081248 → 3.4.2-master-202509090229

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 (29) hide show
  1. package/package.json +4 -4
  2. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +1 -1
  3. package/src/app/routes/_authenticated/_assets/assets.tsx +1 -0
  4. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +1 -1
  5. package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +1 -1
  6. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +1 -1
  7. package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +1 -1
  8. package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +1 -1
  9. package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +1 -1
  10. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +1 -1
  11. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +1 -1
  12. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +2 -2
  13. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +1 -1
  14. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +5 -1
  15. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +1 -1
  16. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +1 -1
  17. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +2 -2
  18. package/src/app/routes/_authenticated/_products/products_.$id.tsx +1 -1
  19. package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +2 -2
  20. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +1 -1
  21. package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +1 -1
  22. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +1 -1
  23. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +1 -1
  24. package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +1 -1
  25. package/src/app/routes/_authenticated/_system/healthchecks.tsx +1 -0
  26. package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +1 -1
  27. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -1
  28. package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +1 -1
  29. package/src/lib/components/layout/generated-breadcrumbs.tsx +103 -43
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vendure/dashboard",
3
3
  "private": false,
4
- "version": "3.4.2-master-202509081248",
4
+ "version": "3.4.2-master-202509090229",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -100,8 +100,8 @@
100
100
  "@types/react": "^19.0.10",
101
101
  "@types/react-dom": "^19.0.4",
102
102
  "@uidotdev/usehooks": "^2.4.1",
103
- "@vendure/common": "^3.4.2-master-202509081248",
104
- "@vendure/core": "^3.4.2-master-202509081248",
103
+ "@vendure/common": "^3.4.2-master-202509090229",
104
+ "@vendure/core": "^3.4.2-master-202509090229",
105
105
  "@vitejs/plugin-react": "^4.3.4",
106
106
  "acorn": "^8.11.3",
107
107
  "acorn-walk": "^8.3.2",
@@ -152,5 +152,5 @@
152
152
  "lightningcss-linux-arm64-musl": "^1.29.3",
153
153
  "lightningcss-linux-x64-musl": "^1.29.1"
154
154
  },
155
- "gitHead": "677040a4acf1ee12e1cd2f46fb75021d37954271"
155
+ "gitHead": "05e55ae78c8486c39c8b58dfae290777e648398e"
156
156
  }
@@ -36,7 +36,7 @@ export const Route = createFileRoute('/_authenticated/_administrators/administra
36
36
  breadcrumb: (isNew, entity) => {
37
37
  const name = `${entity?.firstName} ${entity?.lastName}`;
38
38
  return [
39
- { path: '/administrators', label: 'Administrators' },
39
+ { path: '/administrators', label: <Trans>Administrators</Trans> },
40
40
  isNew ? <Trans>New administrator</Trans> : name,
41
41
  ];
42
42
  },
@@ -6,6 +6,7 @@ import { DeleteAssetsBulkAction } from './components/asset-bulk-actions.js';
6
6
 
7
7
  export const Route = createFileRoute('/_authenticated/_assets/assets')({
8
8
  component: RouteComponent,
9
+ loader: () => ({ breadcrumb: () => <Trans>Assets</Trans> }),
9
10
  });
10
11
 
11
12
  function RouteComponent() {
@@ -35,7 +35,7 @@ export const Route = createFileRoute('/_authenticated/_assets/assets_/$id')({
35
35
  queryDocument: assetDetailDocument,
36
36
  breadcrumb(isNew, entity) {
37
37
  return [
38
- { path: '/assets', label: 'Assets' },
38
+ { path: '/assets', label: <Trans>Assets</Trans> },
39
39
  isNew ? <Trans>New asset</Trans> : (entity?.name ?? ''),
40
40
  ];
41
41
  },
@@ -37,7 +37,7 @@ export const Route = createFileRoute('/_authenticated/_channels/channels_/$id')(
37
37
  queryDocument: channelDetailDocument,
38
38
  breadcrumb(isNew, entity) {
39
39
  return [
40
- { path: '/channels', label: 'Channels' },
40
+ { path: '/channels', label: <Trans>Channels</Trans> },
41
41
  isNew ? <Trans>New channel</Trans> : <ChannelCodeLabel code={entity?.code ?? ''} />,
42
42
  ];
43
43
  },
@@ -41,7 +41,7 @@ export const Route = createFileRoute('/_authenticated/_collections/collections_/
41
41
  pageId,
42
42
  queryDocument: collectionDetailDocument,
43
43
  breadcrumb: (isNew, entity) => [
44
- { path: '/collections', label: 'Collections' },
44
+ { path: '/collections', label: <Trans>Collections</Trans> },
45
45
  isNew ? <Trans>New collection</Trans> : entity?.name,
46
46
  ],
47
47
  }),
@@ -31,7 +31,7 @@ export const Route = createFileRoute('/_authenticated/_countries/countries_/$id'
31
31
  pageId,
32
32
  queryDocument: countryDetailDocument,
33
33
  breadcrumb: (isNew, entity) => [
34
- { path: '/countries', label: 'Countries' },
34
+ { path: '/countries', label: <Trans>Countries</Trans> },
35
35
  isNew ? <Trans>New country</Trans> : entity?.name,
36
36
  ],
37
37
  }),
@@ -34,7 +34,7 @@ export const Route = createFileRoute('/_authenticated/_customer-groups/customer-
34
34
  pageId,
35
35
  queryDocument: customerGroupDetailDocument,
36
36
  breadcrumb: (isNew, entity) => [
37
- { path: '/customer-groups', label: 'Customer groups' },
37
+ { path: '/customer-groups', label: <Trans>Customer Groups</Trans> },
38
38
  isNew ? <Trans>New customer group</Trans> : entity?.name,
39
39
  ],
40
40
  }),
@@ -55,7 +55,7 @@ export const Route = createFileRoute('/_authenticated/_customers/customers_/$id'
55
55
  pageId,
56
56
  queryDocument: customerDetailDocument,
57
57
  breadcrumb: (isNew, entity) => [
58
- { path: '/customers', label: 'Customers' },
58
+ { path: '/customers', label: <Trans>Customers</Trans> },
59
59
  isNew ? <Trans>New customer</Trans> : `${entity?.firstName} ${entity?.lastName}`,
60
60
  ],
61
61
  }),
@@ -36,7 +36,7 @@ export const Route = createFileRoute('/_authenticated/_facets/facets_/$facetId/v
36
36
  queryDocument: facetValueDetailDocument,
37
37
  breadcrumb(isNew, entity) {
38
38
  const facetName = entity?.facet.name ?? 'Facet Value';
39
- const breadcrumb: PageBreadcrumb[] = [{ path: '/facets', label: 'Facets' }];
39
+ const breadcrumb: PageBreadcrumb[] = [{ path: '/facets', label: <Trans>Facets</Trans> }];
40
40
  if (isNew) {
41
41
  breadcrumb.push(<Trans>New facet value</Trans>);
42
42
  } else if (entity) {
@@ -32,7 +32,7 @@ export const Route = createFileRoute('/_authenticated/_facets/facets_/$id')({
32
32
  pageId,
33
33
  queryDocument: facetDetailDocument,
34
34
  breadcrumb(isNew, entity) {
35
- return [{ path: '/facets', label: 'Facets' }, isNew ? <Trans>New facet</Trans> : entity?.name];
35
+ return [{ path: '/facets', label: <Trans>Facets</Trans> }, isNew ? <Trans>New facet</Trans> : entity?.name];
36
36
  },
37
37
  }),
38
38
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -38,7 +38,7 @@ export const Route = createFileRoute('/_authenticated/_global-settings/global-se
38
38
  {},
39
39
  );
40
40
  return {
41
- breadcrumb: [{ path: '/global-settings', label: <Trans>Global settings</Trans> }],
41
+ breadcrumb: [{ path: '/global-settings', label: <Trans>Global Settings</Trans> }],
42
42
  };
43
43
  },
44
44
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -88,7 +88,7 @@ function GlobalSettingsPage() {
88
88
  return (
89
89
  <Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
90
90
  <PageTitle>
91
- <Trans>Global settings</Trans>
91
+ <Trans>Global Settings</Trans>
92
92
  </PageTitle>
93
93
  <PageActionBar>
94
94
  <PageActionBarRight>
@@ -71,7 +71,7 @@ export const Route = createFileRoute('/_authenticated/_orders/orders_/$id')({
71
71
  }
72
72
 
73
73
  return {
74
- breadcrumb: [{ path: '/orders', label: 'Orders' }, result.order.code],
74
+ breadcrumb: [{ path: '/orders', label: <Trans>Orders</Trans> }, result.order.code],
75
75
  };
76
76
  },
77
77
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -62,7 +62,11 @@ export const Route = createFileRoute('/_authenticated/_orders/orders_/$id_/modif
62
62
  }
63
63
 
64
64
  return {
65
- breadcrumb: [{ path: '/orders', label: 'Orders' }, result.order.code, { label: 'Modify' }],
65
+ breadcrumb: [
66
+ { path: '/orders', label: <Trans>Orders</Trans> },
67
+ result.order.code,
68
+ { label: <Trans>Modify</Trans> },
69
+ ],
66
70
  };
67
71
  },
68
72
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -68,7 +68,7 @@ export const Route = createFileRoute('/_authenticated/_orders/orders_/draft/$id'
68
68
  }
69
69
 
70
70
  return {
71
- breadcrumb: [{ path: '/orders', label: 'Orders' }, result.order.code],
71
+ breadcrumb: [{ path: '/orders', label: <Trans>Orders</Trans> }, result.order.code],
72
72
  };
73
73
  },
74
74
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -39,7 +39,7 @@ export const Route = createFileRoute('/_authenticated/_payment-methods/payment-m
39
39
  queryDocument: paymentMethodDetailDocument,
40
40
  breadcrumb(_isNew, entity) {
41
41
  return [
42
- { path: '/payment-methods', label: 'Payment methods' },
42
+ { path: '/payment-methods', label: <Trans>Payment Methods</Trans> },
43
43
  _isNew ? <Trans>New payment method</Trans> : entity?.name,
44
44
  ];
45
45
  },
@@ -46,12 +46,12 @@ export const Route = createFileRoute('/_authenticated/_product-variants/product-
46
46
  breadcrumb(_isNew, entity, location) {
47
47
  if ((location.search as any).from === 'product') {
48
48
  return [
49
- { path: '/product', label: 'Products' },
49
+ { path: '/product', label: <Trans>Products</Trans> },
50
50
  { path: `/products/${entity?.product.id}`, label: entity?.product.name ?? '' },
51
51
  entity?.name,
52
52
  ];
53
53
  }
54
- return [{ path: '/product-variants', label: 'Product Variants' }, entity?.name];
54
+ return [{ path: '/product-variants', label: <Trans>Product Variants</Trans> }, entity?.name];
55
55
  },
56
56
  }),
57
57
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -40,7 +40,7 @@ export const Route = createFileRoute('/_authenticated/_products/products_/$id')(
40
40
  queryDocument: productDetailDocument,
41
41
  breadcrumb(isNew, entity) {
42
42
  return [
43
- { path: '/products', label: 'Products' },
43
+ { path: '/products', label: <Trans>Products</Trans> },
44
44
  isNew ? <Trans>New product</Trans> : entity?.name,
45
45
  ];
46
46
  },
@@ -51,9 +51,9 @@ export const Route = createFileRoute('/_authenticated/_products/products_/$id_/v
51
51
  });
52
52
  return {
53
53
  breadcrumb: [
54
- { path: '/products', label: 'Products' },
54
+ { path: '/products', label: <Trans>Products</Trans> },
55
55
  { path: `/products/${params.id}`, label: result.product?.name },
56
- <Trans>Manage variants</Trans>,
56
+ <Trans>Manage Variants</Trans>,
57
57
  ],
58
58
  };
59
59
  },
@@ -40,7 +40,7 @@ export const Route = createFileRoute('/_authenticated/_promotions/promotions_/$i
40
40
  queryDocument: promotionDetailDocument,
41
41
  breadcrumb(isNew, entity) {
42
42
  return [
43
- { path: '/promotions', label: 'Promotions' },
43
+ { path: '/promotions', label: <Trans>Promotions</Trans> },
44
44
  isNew ? <Trans>New promotion</Trans> : entity?.name,
45
45
  ];
46
46
  },
@@ -31,7 +31,7 @@ export const Route = createFileRoute('/_authenticated/_roles/roles_/$id')({
31
31
  queryDocument: roleDetailDocument,
32
32
  breadcrumb(isNew, entity) {
33
33
  return [
34
- { path: '/roles', label: 'Roles' },
34
+ { path: '/roles', label: <Trans>Roles</Trans> },
35
35
  isNew ? <Trans>New role</Trans> : entity?.description,
36
36
  ];
37
37
  },
@@ -28,7 +28,7 @@ export const Route = createFileRoute('/_authenticated/_sellers/sellers_/$id')({
28
28
  pageId,
29
29
  queryDocument: sellerDetailDocument,
30
30
  breadcrumb: (isNew, entity) => [
31
- { path: '/sellers', label: 'Sellers' },
31
+ { path: '/sellers', label: <Trans>Sellers</Trans> },
32
32
  isNew ? <Trans>New seller</Trans> : entity?.name,
33
33
  ],
34
34
  }),
@@ -39,7 +39,7 @@ export const Route = createFileRoute('/_authenticated/_shipping-methods/shipping
39
39
  queryDocument: shippingMethodDetailDocument,
40
40
  breadcrumb(isNew, entity) {
41
41
  return [
42
- { path: '/shipping-methods', label: 'Shipping methods' },
42
+ { path: '/shipping-methods', label: <Trans>Shipping Methods</Trans> },
43
43
  isNew ? <Trans>New shipping method</Trans> : entity?.name,
44
44
  ];
45
45
  },
@@ -35,7 +35,7 @@ export const Route = createFileRoute('/_authenticated/_stock-locations/stock-loc
35
35
  queryDocument: stockLocationDetailQuery,
36
36
  breadcrumb(isNew, entity) {
37
37
  return [
38
- { path: '/stock-locations', label: 'Stock locations' },
38
+ { path: '/stock-locations', label: <Trans>Stock Locations</Trans> },
39
39
  isNew ? <Trans>New stock location</Trans> : entity?.name,
40
40
  ];
41
41
  },
@@ -10,6 +10,7 @@ import { uiConfig } from 'virtual:vendure-ui-config';
10
10
 
11
11
  export const Route = createFileRoute('/_authenticated/_system/healthchecks')({
12
12
  component: HealthchecksPage,
13
+ loader: () => ({ breadcrumb: () => <Trans>Healthchecks</Trans> }),
13
14
  });
14
15
 
15
16
  interface HealthcheckItem {
@@ -35,7 +35,7 @@ export const Route = createFileRoute('/_authenticated/_tax-categories/tax-catego
35
35
  queryDocument: taxCategoryDetailDocument,
36
36
  breadcrumb(isNew, entity) {
37
37
  return [
38
- { path: '/tax-categories', label: 'Tax categories' },
38
+ { path: '/tax-categories', label: <Trans>Tax Categories</Trans> },
39
39
  isNew ? <Trans>New tax category</Trans> : entity?.name,
40
40
  ];
41
41
  },
@@ -34,7 +34,7 @@ export const Route = createFileRoute('/_authenticated/_tax-rates/tax-rates_/$id'
34
34
  queryDocument: taxRateDetailDocument,
35
35
  breadcrumb(isNew, entity) {
36
36
  return [
37
- { path: '/tax-rates', label: 'Tax rates' },
37
+ { path: '/tax-rates', label: <Trans>Tax Rates</Trans> },
38
38
  isNew ? <Trans>New tax rate</Trans> : entity?.name,
39
39
  ];
40
40
  },
@@ -30,7 +30,7 @@ export const Route = createFileRoute('/_authenticated/_zones/zones_/$id')({
30
30
  pageId,
31
31
  queryDocument: zoneDetailDocument,
32
32
  breadcrumb(isNew, entity) {
33
- return [{ path: '/zones', label: 'Zones' }, isNew ? <Trans>New zone</Trans> : entity?.name];
33
+ return [{ path: '/zones', label: <Trans>Zones</Trans> }, isNew ? <Trans>New zone</Trans> : entity?.name];
34
34
  },
35
35
  }),
36
36
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -5,9 +5,11 @@ import {
5
5
  BreadcrumbList,
6
6
  BreadcrumbSeparator,
7
7
  } from '@/vdb/components/ui/breadcrumb.js';
8
- import { Link, useRouterState } from '@tanstack/react-router';
8
+ import { Link, useRouter, useRouterState } from '@tanstack/react-router';
9
9
  import * as React from 'react';
10
10
  import { Fragment } from 'react';
11
+ import { getNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
12
+ import type { NavMenuItem, NavMenuSection } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
11
13
 
12
14
  export interface BreadcrumbPair {
13
15
  label: string | React.ReactElement;
@@ -20,54 +22,112 @@ export type PageBreadcrumb = BreadcrumbPair | BreadcrumbShorthand;
20
22
 
21
23
  export function GeneratedBreadcrumbs() {
22
24
  const matches = useRouterState({ select: s => s.matches });
23
- const breadcrumbs: BreadcrumbPair[] = matches
24
- .filter(match => match.loaderData?.breadcrumb)
25
- .map(({ pathname, loaderData }) => {
26
- if (typeof loaderData.breadcrumb === 'string') {
27
- return {
28
- label: loaderData.breadcrumb,
29
- path: pathname,
30
- };
31
- }
32
- if (Array.isArray(loaderData.breadcrumb)) {
33
- return loaderData.breadcrumb.map((breadcrumb: PageBreadcrumb) => {
34
- if (typeof breadcrumb === 'string') {
35
- return {
36
- label: breadcrumb,
37
- path: pathname,
38
- };
39
- } else if (React.isValidElement(breadcrumb)) {
40
- return {
41
- label: breadcrumb,
42
- path: pathname,
43
- };
44
- } else {
45
- return {
46
- label: breadcrumb.label,
47
- path: breadcrumb.path,
48
- };
49
- }
50
- });
51
- }
52
- if (typeof loaderData.breadcrumb === 'function') {
53
- return {
54
- label: loaderData.breadcrumb(),
55
- path: pathname,
56
- };
25
+ const currentPath = useRouterState({ select: s => s.location.pathname });
26
+ const router = useRouter();
27
+ const navMenuConfig = getNavMenuConfig();
28
+ const basePath = router.basepath || '';
29
+
30
+ const normalizeBreadcrumb = (breadcrumb: any, pathname: string): BreadcrumbPair[] => {
31
+ if (typeof breadcrumb === 'string') {
32
+ return [{ label: breadcrumb, path: pathname }];
33
+ }
34
+ if (React.isValidElement(breadcrumb)) {
35
+ return [{ label: breadcrumb, path: pathname }];
36
+ }
37
+ if (typeof breadcrumb === 'function') {
38
+ return [{ label: breadcrumb(), path: pathname }];
39
+ }
40
+ if (Array.isArray(breadcrumb)) {
41
+ return breadcrumb.map((crumb: PageBreadcrumb) => {
42
+ if (typeof crumb === 'string' || React.isValidElement(crumb)) {
43
+ return { label: crumb, path: pathname };
44
+ }
45
+ return { label: crumb.label, path: crumb.path };
46
+ });
47
+ }
48
+ return [];
49
+ };
50
+
51
+ const rawCrumbs: BreadcrumbPair[] = React.useMemo(() => {
52
+ return matches
53
+ .filter(match => match.loaderData?.breadcrumb)
54
+ .flatMap(({ pathname, loaderData }) =>
55
+ normalizeBreadcrumb(loaderData.breadcrumb, pathname)
56
+ );
57
+ }, [matches]);
58
+
59
+ const isBaseRoute = (p: string) => p === basePath || p === `${basePath}/`;
60
+ const pageCrumbs: BreadcrumbPair[] = rawCrumbs.filter(c => !isBaseRoute(c.path));
61
+
62
+ const normalizePath = (path: string): string => {
63
+ const normalizedPath = basePath && path.startsWith(basePath) ? path.slice(basePath.length) : path;
64
+ return normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`;
65
+ };
66
+
67
+ const pathMatches = (cleanPath: string, rawUrl?: string): boolean => {
68
+ if (!rawUrl) return false;
69
+ const strip = (p: string) => (p !== '/' && p.endsWith('/') ? p.slice(0, -1) : p);
70
+ const p = strip(cleanPath);
71
+ const u = strip(rawUrl);
72
+ return p === u || p.startsWith(`${u}/`);
73
+ };
74
+
75
+ const checkSectionItems = (
76
+ section: NavMenuSection | NavMenuItem,
77
+ cleanPath: string,
78
+ ): BreadcrumbPair | undefined => {
79
+ if (!('items' in section) || !Array.isArray(section.items)) {
80
+ return undefined;
81
+ }
82
+
83
+ for (const item of section.items) {
84
+ if (!item?.url) continue;
85
+ if (pathMatches(cleanPath, item.url)) {
86
+ return { label: section.title, path: item.url };
57
87
  }
58
- if (React.isValidElement(loaderData.breadcrumb)) {
59
- return {
60
- label: loaderData.breadcrumb,
61
- path: pathname,
62
- };
88
+ }
89
+ return undefined;
90
+ };
91
+
92
+ const checkDirectSection = (
93
+ section: NavMenuSection | NavMenuItem,
94
+ cleanPath: string,
95
+ ): BreadcrumbPair | undefined => {
96
+ if ('url' in section && section.url && pathMatches(cleanPath, section.url)) {
97
+ return { label: section.title, path: section.url };
98
+ }
99
+ return undefined;
100
+ };
101
+
102
+ const findSectionCrumb = (path: string): BreadcrumbPair | undefined => {
103
+ const cleanPath = normalizePath(path);
104
+ const sections: Array<NavMenuSection | NavMenuItem> = navMenuConfig?.sections ?? [];
105
+ if (sections.length === 0) return undefined;
106
+
107
+ for (const section of sections) {
108
+ const result = checkSectionItems(section, cleanPath) || checkDirectSection(section, cleanPath);
109
+ if (result) {
110
+ return result;
63
111
  }
64
- })
65
- .flat();
112
+ }
113
+ return undefined;
114
+ };
115
+
116
+ const sectionCrumb = React.useMemo(
117
+ () => findSectionCrumb(currentPath),
118
+ [currentPath, basePath, navMenuConfig],
119
+ );
120
+ const breadcrumbs: BreadcrumbPair[] = React.useMemo(() => {
121
+ const arr = sectionCrumb ? [sectionCrumb, ...pageCrumbs] : pageCrumbs;
122
+ return arr.filter((c, i, self) =>
123
+ self.findIndex(x => x.path === c.path && x.label === c.label) === i,
124
+ );
125
+ }, [sectionCrumb, pageCrumbs]);
66
126
  return (
67
127
  <Breadcrumb>
68
128
  <BreadcrumbList>
69
129
  {breadcrumbs.map(({ label, path }, index, arr) => (
70
- <Fragment key={index}>
130
+ <Fragment key={`${path}-${index}`}>
71
131
  <BreadcrumbItem className="hidden md:block">
72
132
  <BreadcrumbLink asChild>
73
133
  <Link to={path}>{label}</Link>