Skip to content

Commit a69fe96

Browse files
qxoSteKoe
andauthored
feat: views/instances/startup UI add result filter and sort by duration desc (#2835)
filter by name/startupStep.name/startupStep.tags and if the filter input is number will filter by duration and the result will be sorted by duration desc. Co-authored-by: Stephan Köninger <stephan.koeninger@codecentric.de>
1 parent d733b11 commit a69fe96

File tree

2 files changed

+131
-30
lines changed

2 files changed

+131
-30
lines changed

spring-boot-admin-server-ui/src/main/frontend/views/instances/startup/index.vue

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,51 @@
1818
<sba-instance-section :error="error">
1919
<template #before>
2020
<sba-sticky-subnav>
21-
<div class="inline-flex items-center">
22-
<div class="mx-3">
23-
<sba-button v-if="!isExpanded" @click="expandTree">
24-
<svg
25-
class="h-4 w-4"
26-
fill="currentColor"
27-
viewBox="0 0 16 16"
28-
xmlns="http://www.w3.org/2000/svg"
29-
>
30-
<path
31-
d="M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8zM7.646.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 1.707V5.5a.5.5 0 0 1-1 0V1.707L6.354 2.854a.5.5 0 1 1-.708-.708l2-2zM8 10a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 14.293V10.5A.5.5 0 0 1 8 10z"
32-
fill-rule="evenodd"
33-
/>
34-
</svg>
35-
</sba-button>
36-
<sba-button v-if="isExpanded" @click="expandTree">
37-
<svg
38-
class="h-4 w-4"
39-
fill="currentColor"
40-
viewBox="0 0 16 16"
41-
xmlns="http://www.w3.org/2000/svg"
42-
>
43-
<path
44-
d="M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8zm7-8a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 1 1 .708-.708L7.5 4.293V.5A.5.5 0 0 1 8 0zm-.5 11.707-1.146 1.147a.5.5 0 0 1-.708-.708l2-2a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 11.707V15.5a.5.5 0 0 1-1 0v-3.793z"
45-
fill-rule="evenodd"
46-
/>
47-
</svg>
48-
</sba-button>
21+
<div class="flex gap-2">
22+
<sba-button v-if="!isExpanded" @click="expandTree">
23+
<svg
24+
class="h-4 w-4"
25+
fill="currentColor"
26+
viewBox="0 0 16 16"
27+
xmlns="http://www.w3.org/2000/svg"
28+
>
29+
<path
30+
d="M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8zM7.646.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 1.707V5.5a.5.5 0 0 1-1 0V1.707L6.354 2.854a.5.5 0 1 1-.708-.708l2-2zM8 10a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 14.293V10.5A.5.5 0 0 1 8 10z"
31+
fill-rule="evenodd"
32+
/>
33+
</svg>
34+
</sba-button>
35+
<sba-button v-if="isExpanded" @click="expandTree">
36+
<svg
37+
class="h-4 w-4"
38+
fill="currentColor"
39+
viewBox="0 0 16 16"
40+
xmlns="http://www.w3.org/2000/svg"
41+
>
42+
<path
43+
d="M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8zm7-8a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 1 1 .708-.708L7.5 4.293V.5A.5.5 0 0 1 8 0zm-.5 11.707-1.146 1.147a.5.5 0 0 1-.708-.708l2-2a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 11.707V15.5a.5.5 0 0 1-1 0v-3.793z"
44+
fill-rule="evenodd"
45+
/>
46+
</svg>
47+
</sba-button>
48+
<div class="flex-1">
49+
<sba-input
50+
v-model="filter"
51+
:placeholder="
52+
$t('term.filter') +
53+
' by name/tags(key,value) or number for filter(duration)'
54+
"
55+
name="filter"
56+
type="search"
57+
>
58+
<template #prepend>
59+
<font-awesome-icon icon="filter" />
60+
</template>
61+
<template #append>
62+
<span v-text="filteredSize" /> /
63+
<span v-text="totalSize" />
64+
</template>
65+
</sba-input>
4966
</div>
5067
</div>
5168
</sba-sticky-subnav>
@@ -57,6 +74,8 @@
5774
v-if="hasLoaded"
5875
:expand="expandedNodes"
5976
:tree="eventTree"
77+
:filter="filter"
78+
@after-filter-action="afterFilterAction"
6079
@change="saveTreeState"
6180
/>
6281
</div>
@@ -85,6 +104,9 @@ export default {
85104
expandedNodes: null,
86105
eventTree: null,
87106
isExpanded: false,
107+
filter: null,
108+
filteredSize: '',
109+
totalSize: '',
88110
}),
89111
async created() {
90112
await this.fetchStartup();
@@ -111,11 +133,16 @@ export default {
111133
try {
112134
const res = await this.instance.fetchStartup();
113135
this.eventTree = StartupActuatorService.parseAsTree(res.data);
136+
this.totalSize = this.eventTree.getEvents().length;
137+
this.filteredSize = this.totalSize;
114138
} catch (error) {
115139
console.warn('Fetching startup failed:', error);
116140
this.error = error;
117141
}
118142
},
143+
afterFilterAction(filteredSize) {
144+
this.filteredSize = filteredSize;
145+
},
119146
expandEventId() {
120147
let queryParams = this.$router.currentRoute.query;
121148
if (queryParams && queryParams.id) {

spring-boot-admin-server-ui/src/main/frontend/views/instances/startup/tree-table.vue

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</div>
2929
<ul v-if="tree">
3030
<tree-item
31-
v-for="(eventNode, index) in tree.getRoots()"
31+
v-for="(eventNode, index) in filteredTree"
3232
:key="index"
3333
:item="eventNode"
3434
:expand="expandedNodes"
@@ -39,12 +39,57 @@
3939
</template>
4040

4141
<script>
42+
import orderBy from 'lodash/orderBy';
43+
4244
import { StartupActuatorEventTree } from '@/services/startup-activator-tree';
4345
import TreeItem from '@/views/instances/startup/tree-item';
4446
47+
const filterProperty = (needle) => (property, name) => {
48+
if ('tags' == name && property && property.length > 0) {
49+
return property.find((x) => findByFilter(needle, x));
50+
}
51+
return (
52+
name.toString().toLowerCase().includes(needle) ||
53+
(property && property.toString().toLowerCase().includes(needle))
54+
);
55+
};
56+
57+
const findByFilter = (needle, properties, keys) => {
58+
if (!properties) {
59+
return false;
60+
}
61+
const fn1 = filterProperty(needle);
62+
if (!keys) {
63+
keys = Object.keys(properties);
64+
}
65+
return keys.find((key) => {
66+
return fn1(properties[key], key);
67+
});
68+
};
69+
70+
const filterResults = (needle) => (startupEvent) => {
71+
if (!startupEvent || !startupEvent.startupStep) {
72+
return null;
73+
}
74+
if (typeof needle == 'function') {
75+
return needle(startupEvent) ? startupEvent : null;
76+
}
77+
var ret =
78+
findByFilter(needle, startupEvent, ['name']) ||
79+
findByFilter(needle, startupEvent.startupStep, ['name', 'tags']);
80+
if (ret) {
81+
return startupEvent;
82+
}
83+
return null;
84+
};
85+
4586
export default {
4687
components: { TreeItem },
4788
props: {
89+
filter: {
90+
type: String,
91+
default: '',
92+
},
4893
tree: {
4994
type: StartupActuatorEventTree,
5095
required: true,
@@ -55,15 +100,38 @@ export default {
55100
default: null,
56101
},
57102
},
58-
emits: ['change'],
103+
emits: ['change', 'after-filter-action'],
59104
data: () => ({
60105
expandedNodes: new Set(),
61106
isExpanded: false,
107+
resultSize: '',
62108
}),
63109
computed: {
64110
treeSize() {
65111
return new Set(this.tree.getEvents()).size;
66112
},
113+
filteredTree() {
114+
if (!this.tree) {
115+
return [];
116+
}
117+
if (!this.filter) {
118+
return this.tree.getRoots();
119+
}
120+
var xfilter;
121+
if (/^[0-9.]+$/.test(this.filter)) {
122+
const timeLimt = parseFloat(this.filter);
123+
xfilter = function (x) {
124+
return x.duration >= timeLimt;
125+
};
126+
} else {
127+
xfilter = this.filter.toLowerCase();
128+
}
129+
const results = this.tree
130+
.getEvents()
131+
.map(filterResults(xfilter))
132+
.filter((ps) => ps && Object.keys(ps.startupStep).length > 0);
133+
return orderBy(results, (o) => -o.duration);
134+
},
67135
},
68136
watch: {
69137
expand(expandedNodes) {
@@ -74,6 +142,12 @@ export default {
74142
expandedNodes: this.expandedNodes,
75143
});
76144
},
145+
filteredTree: {
146+
deep: true,
147+
handler: function (newVal) {
148+
this.$emit('after-filter-action', newVal.length);
149+
},
150+
},
77151
},
78152
created() {
79153
if (this.expand) {

0 commit comments

Comments
 (0)