Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#2589): allow to set or override group icons in the instance sidebar #2608

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions spring-boot-admin-docs/src/site/asciidoc/customize_ui.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,34 @@ include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=cust
<1> Name of the view and the route.
<2> Path in Vue router.
<3> The imported custom component, which will be rendered on the route.
<4> The handle for the custom view to be shown in the top navigation bar.
<5> Order for the view.
<6> Using `i18n.mergeLocaleMessage` allows to add custom translations.
<4> An optional group name that allows to bind views to a logical group (defaults to "none")
<5> The handle for the custom view to be shown in the top navigation bar.
<6> Order for the view.
<7> Using `i18n.mergeLocaleMessage` allows to add custom translations.

Views in the top navigation bar are sorted by ascending order.

If new top level routes are added to the frontend, they also must be known to the backend.
Add a `/META-INF/spring-boot-admin-server-ui/extensions/{name}/routes.txt` with all your new toplevel routes (one route per line).

Groups are used in instance sidebar to aggregate multiple views into a collapsible menu entry showing the group's name.
When a group contains just a single element, the label of the view is shown instead of the group's name.

==== Override/Set custom group icons ====
In order to override or set icons for (custom) groups you can use the `SBA.viewRegistry.setGroupIcon` function as follows:

[source,javascript]
----
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=customization-ui-groups]
----
<1> Name of the group to set icon for
<2> Arbitrary HTML code (e.g. SVG image) that is inserted and parsed as icon.

[[customizing-custom-views-top-level]]
==== Adding a Top-Level View ====

Here is a simple top level view just listing all registered applications:
[source,html]

----
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/custom.vue[lines=17..-1]
----
Expand Down Expand Up @@ -109,8 +122,8 @@ include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/custom-endpoint.vu
<2> Each instance has a preconfigured https://github.com/axios/axios[axios] instance to access the endpoints with the correct path and headers.

Registering the instance view works like for the top-level view with some additional properties:
[source,javascript]

[source,javascript]
----
include::{samples-dir}/spring-boot-admin-sample-custom-ui/src/index.js[tags=customization-ui-endpoint]
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ SBA.use({
name: "custom", //<1>
path: "/custom", //<2>
component: custom, //<3>
handle, //<4>
order: 1000, //<5>
group: "custom", //<4>
handle, //<5>
order: 1000, //<6>
});
i18n.mergeLocaleMessage("en", {
custom: {
label: "My Extensions", //<6>
label: "My Extensions", //<7>
},
});
i18n.mergeLocaleMessage("de", {
Expand Down Expand Up @@ -78,3 +79,13 @@ SBA.viewRegistry.addView({
}, // <3>
});
// end::customization-ui-endpoint[]

// tag::customization-ui-groups[]
SBA.viewRegistry.setGroupIcon(
"custom", //<1>
`<svg xmlns='http://www.w3.org/2000/svg'
class='h-5 mr-3'
viewBox='0 0 576 512'><path d='M512 80c8.8 0 16 7.2 16 16V416c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16H512zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 208c14.2 0 27 6.1 35.8 16c8.8 9.9 24 10.7 33.9 1.9s10.7-24 1.9-33.9c-17.5-19.6-43.1-32-71.5-32c-53 0-96 43-96 96s43 96 96 96c28.4 0 54-12.4 71.5-32c8.8-9.9 8-25-1.9-33.9s-25-8-33.9 1.9c-8.8 9.9-21.6 16-35.8 16c-26.5 0-48-21.5-48-48s21.5-48 48-48zm144 48c0-26.5 21.5-48 48-48c14.2 0 27 6.1 35.8 16c8.8 9.9 24 10.7 33.9 1.9s10.7-24 1.9-33.9c-17.5-19.6-43.1-32-71.5-32c-53 0-96 43-96 96s43 96 96 96c28.4 0 54-12.4 71.5-32c8.8-9.9 8-25-1.9-33.9s-25-8-33.9 1.9c-8.8 9.9-21.6 16-35.8 16c-26.5 0-48-21.5-48-48z'/>
</svg>` //<2>
);
// end::customization-ui-groups[]
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const emitCustomRouteAddedEvent = debounce(() => {
export function useViewRegistry() {
return {
views: viewRegistry.views,
setGroupIcon(name, icon) {
viewRegistry.setGroupIcon(name, icon);
},
addView(viewToAdd) {
const view = viewRegistry.addView(viewToAdd)[0];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Text, VNode, h, markRaw, reactive, shallowRef, toRaw } from 'vue';
import { Router, createRouter, createWebHistory } from 'vue-router';

import sbaConfig from './sba-config';
import { VIEW_GROUP } from './views/ViewGroup.js';
import { VIEW_GROUP, VIEW_GROUP_ICON } from './views/ViewGroup.js';

let router: Router;

Expand Down Expand Up @@ -54,6 +54,10 @@ export default class ViewRegistry {
return router;
}

setGroupIcon(name, icon) {
VIEW_GROUP_ICON[name] = icon;
}

createRouter() {
const routesKnownToBackend = sbaConfig.uiSettings.routes.map(
(r) => new RegExp(`^${r.replace('/**', '(/.*)?')}$`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,19 @@ export const VIEW_GROUP = {
NONE: 'none',
SECURITY: 'security',
};

export const VIEW_GROUP_ICON = {
[VIEW_GROUP.WEB]:
'<svg aria-hidden="true" focusable="false" data-prefix="fas" class="w-5 h-5 mr-3" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M336.5 160C322 70.7 287.8 8 248 8s-74 62.7-88.5 152h177zM152 256c0 22.2 1.2 43.5 3.3 64h185.3c2.1-20.5 3.3-41.8 3.3-64s-1.2-43.5-3.3-64H155.3c-2.1 20.5-3.3 41.8-3.3 64zm324.7-96c-28.6-67.9-86.5-120.4-158-141.6 24.4 33.8 41.2 84.7 50 141.6h108zM177.2 18.4C105.8 39.6 47.8 92.1 19.3 160h108c8.7-56.9 25.5-107.8 49.9-141.6zM487.4 192H372.7c2.1 21 3.3 42.5 3.3 64s-1.2 43-3.3 64h114.6c5.5-20.5 8.6-41.8 8.6-64s-3.1-43.5-8.5-64zM120 256c0-21.5 1.2-43 3.3-64H8.6C3.2 212.5 0 233.8 0 256s3.2 43.5 8.6 64h114.6c-2-21-3.2-42.5-3.2-64zm39.5 96c14.5 89.3 48.7 152 88.5 152s74-62.7 88.5-152h-177zm159.3 141.6c71.4-21.2 129.4-73.7 158-141.6h-108c-8.8 56.9-25.6 107.8-50 141.6zM19.3 352c28.6 67.9 86.5 120.4 158 141.6-24.4-33.8-41.2-84.7-50-141.6h-108z"></path></svg>',
[VIEW_GROUP.INSIGHTS]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
[VIEW_GROUP.DATA]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>',
[VIEW_GROUP.JVM]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
[VIEW_GROUP.LOGGING]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>',
[VIEW_GROUP.NONE]: '<div class="h-5 w-5 mr-3">&nbsp;</div>',
[VIEW_GROUP.SECURITY]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>',
};
17 changes: 0 additions & 17 deletions spring-boot-admin-server-ui/src/main/frontend/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { VIEW_GROUP } from './ViewGroup.js';

const isStorybook = Object.prototype.hasOwnProperty.call(window, 'STORIES');

Expand All @@ -36,20 +35,4 @@ if (!isStorybook) {
});
}

export const VIEW_GROUP_ICON = {
[VIEW_GROUP.WEB]:
'<svg aria-hidden="true" focusable="false" data-prefix="fas" class="w-4 h-4 mr-3" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M336.5 160C322 70.7 287.8 8 248 8s-74 62.7-88.5 152h177zM152 256c0 22.2 1.2 43.5 3.3 64h185.3c2.1-20.5 3.3-41.8 3.3-64s-1.2-43.5-3.3-64H155.3c-2.1 20.5-3.3 41.8-3.3 64zm324.7-96c-28.6-67.9-86.5-120.4-158-141.6 24.4 33.8 41.2 84.7 50 141.6h108zM177.2 18.4C105.8 39.6 47.8 92.1 19.3 160h108c8.7-56.9 25.5-107.8 49.9-141.6zM487.4 192H372.7c2.1 21 3.3 42.5 3.3 64s-1.2 43-3.3 64h114.6c5.5-20.5 8.6-41.8 8.6-64s-3.1-43.5-8.5-64zM120 256c0-21.5 1.2-43 3.3-64H8.6C3.2 212.5 0 233.8 0 256s3.2 43.5 8.6 64h114.6c-2-21-3.2-42.5-3.2-64zm39.5 96c14.5 89.3 48.7 152 88.5 152s74-62.7 88.5-152h-177zm159.3 141.6c71.4-21.2 129.4-73.7 158-141.6h-108c-8.8 56.9-25.6 107.8-50 141.6zM19.3 352c28.6 67.9 86.5 120.4 158 141.6-24.4-33.8-41.2-84.7-50-141.6h-108z"></path></svg>',
[VIEW_GROUP.INSIGHTS]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
[VIEW_GROUP.DATA]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path></svg>',
[VIEW_GROUP.JVM]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>',
[VIEW_GROUP.LOGGING]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>',
[VIEW_GROUP.NONE]: '',
[VIEW_GROUP.SECURITY]:
'<svg xmlns="http://www.w3.org/2000/svg" class="h-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>',
};

export default views;
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ import SbaButton from '@/components/sba-button.vue';
import Application from '@/services/application';
import Instance from '@/services/instance';
import { compareBy } from '@/utils/collections';
import { VIEW_GROUP_ICON } from '@/views';
import { VIEW_GROUP_ICON } from '@/views/ViewGroup';

export default defineComponent({
components: { SbaButton },
Expand Down