diff --git a/.changeset/mean-mugs-sparkle.md b/.changeset/mean-mugs-sparkle.md new file mode 100644 index 0000000000..7e10e6e431 --- /dev/null +++ b/.changeset/mean-mugs-sparkle.md @@ -0,0 +1,6 @@ +--- +"@medusajs/admin-vite-plugin": patch +"@medusajs/dashboard": patch +--- + +feat(admin-vite-plugin,dashboard): support for react-router's splat and optional segments diff --git a/packages/admin/admin-vite-plugin/src/routes/helpers.ts b/packages/admin/admin-vite-plugin/src/routes/helpers.ts index 22e97c641a..6c9be76fea 100644 --- a/packages/admin/admin-vite-plugin/src/routes/helpers.ts +++ b/packages/admin/admin-vite-plugin/src/routes/helpers.ts @@ -4,7 +4,11 @@ export function getRoute(file: string): string { const importPath = normalizePath(file) return importPath .replace(/.*\/admin\/(routes)/, "") - .replace(/\[([^\]]+)\]/g, ":$1") + .replace("[[*]]", "*?") // optional splat + .replace("[*]", "*") // splat + .replace(/\(([^\[\]\)]+)\)/g, "$1?") // optional static, (foo) + .replace(/\[\[([^\]]+)\]\]/g, ":$1?") // optional dynamic, [[foo]] + .replace(/\[([^\]]+)\]/g, ":$1") // dynamic, [foo] .replace( new RegExp( `/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join( diff --git a/packages/admin/dashboard/src/dashboard-app/dashboard-app.tsx b/packages/admin/dashboard/src/dashboard-app/dashboard-app.tsx index 294ae330c0..de3126a839 100644 --- a/packages/admin/dashboard/src/dashboard-app/dashboard-app.tsx +++ b/packages/admin/dashboard/src/dashboard-app/dashboard-app.tsx @@ -42,6 +42,13 @@ type DashboardAppProps = { plugins: DashboardPlugin[] } +/** + * Matches segments that are optional and at the end of the path. + * Example: /path/to/:id? + * Such paths can be added to the menu items without the optional segment. + */ +const OPTIONAL_LAST_SEGMENT_MATCH = /\/([^\/])+\?$/ + export class DashboardApp { private widgets: WidgetMap private menus: MenuMap @@ -129,10 +136,11 @@ export class DashboardApp { allMenuItems.sort((a, b) => a.path.length - b.path.length) allMenuItems.forEach((item) => { - if (item.path.includes("/:")) { + item.path = item.path.replace(OPTIONAL_LAST_SEGMENT_MATCH, "") + if (item.path.includes("/:") || item.path.endsWith("/*")) { if (process.env.NODE_ENV === "development") { console.warn( - `[@medusajs/dashboard] Menu item for path "${item.path}" can't be added to the sidebar as it contains a parameter.` + `[@medusajs/dashboard] Menu item for path "${item.path}" can't be added to the sidebar as it contains a mandatory parameter.` ) } return diff --git a/packages/admin/dashboard/src/dashboard-app/routes/utils.ts b/packages/admin/dashboard/src/dashboard-app/routes/utils.ts index 9f3cd47bea..450b0dcf6a 100644 --- a/packages/admin/dashboard/src/dashboard-app/routes/utils.ts +++ b/packages/admin/dashboard/src/dashboard-app/routes/utils.ts @@ -37,9 +37,10 @@ const createBranchRoute = (segment: string): RouteObject => ({ const createLeafRoute = ( Component: ComponentType, loader?: LoaderFunction, - handle?: object + handle?: object, + path: string = "" ): RouteObject => ({ - path: "", + path, ErrorBoundary: ErrorBoundary, async lazy() { const result: { @@ -149,7 +150,6 @@ const addRoute = ( if (isComponentSegment || remainingSegments.length === 0) { route.children ||= [] - const leaf = createLeafRoute(Component, loader) if (handle) { route.handle = handle @@ -159,9 +159,17 @@ const addRoute = ( route.loader = loader } - leaf.children = processParallelRoutes(parallelRoutes, currentFullPath) - route.children.push(leaf) - + // Since splat segments must occur at the end of a route, react-router enforces the segment to be a leaf. + // Therefore we can't create a child leaf route with `path: ""` and must instead modify the route itself + if (currentSegment === "*?" || currentSegment === "*") { + const leaf = createLeafRoute(Component, loader, handle, currentSegment) + leaf.children = processParallelRoutes(parallelRoutes, currentFullPath) + Object.assign(route, leaf) + } else { + const leaf = createLeafRoute(Component, loader) + leaf.children = processParallelRoutes(parallelRoutes, currentFullPath) + route.children.push(leaf) + } if (remainingSegments.length > 0) { addRoute( remainingSegments,