Conversation
shreddedbacon
left a comment
There was a problem hiding this comment.
Some things I've found, but there are some things we're working on that may change related to the sidebar and how items may get added
| const navItem = { title: extItem.label, url: extItem.href, icon: resolveIcon(extItem.icon) }; | ||
| if (extItem.position === 'start') { | ||
| items[idx].sectionItems.unshift(navItem); | ||
| } else { | ||
| items[idx].sectionItems.push(navItem); | ||
| } |
There was a problem hiding this comment.
We're aware that there are some issues with the current sidebar implementation, and are working on a different way the sidebar is managed that will probably change how what I've made in this suggestion work. Probably ignore this suggestion for now until that other work goes through properly.
In saying that, the sidebar doesn't render properly when using project-tabs in the provided example, but also doesn't render properly when using sidebar-projects without changes below
Note: also pretty sure project-tabs,environment-tabs,organization-tabs, and settings-tabs are deprecated, so probably don't need to exist.
| const navItem = { title: extItem.label, url: extItem.href, icon: resolveIcon(extItem.icon) }; | |
| if (extItem.position === 'start') { | |
| items[idx].sectionItems.unshift(navItem); | |
| } else { | |
| items[idx].sectionItems.push(navItem); | |
| } | |
| let href = extItem.href | |
| if (projectSlug) { | |
| href = href.replace('[projectSlug]', projectSlug as string ) | |
| } | |
| if (environmentSlug) { | |
| href = href.replace('[environmentSlug]', environmentSlug as string ) | |
| } | |
| const navItem = { title: extItem.label, url: href, icon: resolveIcon(extItem.icon)}; | |
| switch (target) { | |
| case 'sidebar-projects': | |
| console.log(items[idx].sectionItems[0]) | |
| if (items[idx].sectionItems[0].children) { | |
| if (extItem.subTarget == 'environment') { | |
| if (items[idx].sectionItems[0].children[0].children) { | |
| if (items[idx].sectionItems[0].children[0].children[0].children) { | |
| if (extItem.position === 'start') { | |
| items[idx].sectionItems[0].children[0].children[0].children[0].children?.unshift(navItem); | |
| } else { | |
| items[idx].sectionItems[0].children[0].children[0].children[0].children?.push(navItem); | |
| } | |
| } | |
| } | |
| } else { | |
| if (extItem.position === 'start') { | |
| items[idx].sectionItems[0].children[0].children?.unshift(navItem); | |
| } else { | |
| items[idx].sectionItems[0].children[0].children?.push(navItem); | |
| } | |
| } | |
| } | |
| break; | |
| default: | |
| if (extItem.position === 'start') { | |
| items[idx].sectionItems.unshift(navItem); | |
| } else { | |
| items[idx].sectionItems.push(navItem); | |
| } | |
| break; | |
| } |
This would need further work to properly support if needing to extend adding to the environments or others too.
diff --git a/src/lib/extensions/types.ts b/src/lib/extensions/types.ts
index c75187d3..46a81701 100644
--- a/src/lib/extensions/types.ts
+++ b/src/lib/extensions/types.ts
@@ -8,6 +8,9 @@ export type ExtensionNavTarget =
| 'organization-tabs'
| 'settings-tabs';
+export type ExtensionNavSubTarget =
+ | 'environment';
+
export type ExtensionSlotLocation =
| 'project-header'
| 'project-footer'
@@ -24,6 +27,7 @@ export type ExtensionNavItem = {
href: string;
icon?: string;
target: ExtensionNavTarget;
+ subTarget: ExtensionNavSubTarget;
position?: 'start' | 'end' | number;
requiredRoles?: string[];
excludeRoles?: string[];
There was a problem hiding this comment.
Ok cool, have cleaned up the deprecated *-tabs things and fixed up the slug replacement given they seem like the safe changes here
There was a problem hiding this comment.
This can only ever work for platform level roles, not general user roles.
Lagoon doesn't inject general user roles into the token.
It is also broken, but this is because the current branch doesn't inject the roles from the token properly.
There was a problem hiding this comment.
Yeah, left this as-is for now on the assumption the platform level roles (at least) come through properly at some point before merge. Obviously more ideal if we can get user roles to tap into too.
Co-authored-by: Ben Jackson <shreddedbacon@users.noreply.github.com>
Summary
Adds a pluggable extension system that lets downstream deployments inject pages, navigation items, and sidebar sections into Lagoon UI without modifying core code.
Extensions are defined as directories under
extensions/, each with anextension.jsonmanifest. At build time,build-extensionscopies pages and components into the app and generates a mergedextensions.json. At runtime, theExtensionProviderreads this manifest and makes it available to navigation components.Included change
scripts/build-extensions.ts) discovers extensions, copies pages/components/lib into the app tree, writes merged manifestsrc/lib/extensions/schema.ts) validatesextension.jsonat load timesrc/lib/extensions/loader.ts) readsextensions.jsonand provides it viaExtensionContextuseSidenavItems), project tabs (ProjectNavTabs), environment tabs (EnvironmentNavTabs) all pick up extension nav itemssrc/lib/extensions/rbac.ts,ExtensionRouteGuard) role-based access on nav items and pages viarequiredRoles/excludeRolessrc/lib/extensions/icons.ts) maps Lucide icon names from manifests to componentsExtension structure
Manifest format
{ "meta": { "name": "my-extension", "version": "1.0.0" }, "navigation": { "items": [ { "id": "my-page", "label": "My Page", "href": "/my-page", "icon": "Star", "target": "sidebar-projects" } ], "sections": [ { "section": "My Section", "position": "end", "items": [ { "id": "item1", "label": "Item", "href": "/item", "icon": "Star" } ] } ] }, "pages": [ { "route": "my-page", "requiredRoles": ["admin"] } ] }Navigation targets
sidebar-projectssidebar-deploymentssidebar-organizationssidebar-settingsproject-tabsenvironment-tabsDocker usage
Downstream images will need to optionally inject extensions before build:
Working example
A "functional" analytics dashboard example available here:
feat/extensions-example:extensions/analytics/extension.json- manifest with sidebar section, project tab, RBAC rulesextensions/analytics/pages/analytics/page.tsx- global dashboard with chartsextensions/analytics/pages/projects/[projectSlug]/analytics/page.tsxproject-level analytics pageextensions/analytics/components/StatCard, ChartCard components