Skip to content

Commit

Permalink
feat(#1396): implement filter and search function for wallboard (#2612)
Browse files Browse the repository at this point in the history
closes #1396
  • Loading branch information
SteKoe committed Jul 21, 2023
1 parent 42e2271 commit 6dae843
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 45 deletions.
9 changes: 9 additions & 0 deletions spring-boot-admin-server-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion spring-boot-admin-server-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,16 @@
"d3-time": "3.1.0",
"event-source-polyfill": "1.0.31",
"file-saver": "2.0.5",
"fuse.js": "^6.6.2",
"iso8601-duration": "2.1.1",
"lodash-es": "4.17.21",
"mitt": "^3.0.0",
"moment": "2.29.4",
"popper.js": "1.16.1",
"pretty-bytes": "6.1.0",
"random-string": "0.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"random-string": "0.2.0",
"resize-observer-polyfill": "1.5.1",
"rxjs": "7.8.1",
"uuid": "9.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
:readonly="readonly"
:type="type"
:value="modelValue"
class="focus:z-10 p-2 relative flex-1 block w-full rounded-none sm:text-sm bg-opacity-40 backdrop-blur-sm"
class="focus:z-10 p-2 relative flex-1 block w-full rounded-none bg-opacity-40 backdrop-blur-sm"
@input="handleInput"
/>
<!-- APPEND -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="flex text-center h-full">
<div class="flex text-center h-full items-center">
<svg
:class="classNames(sizeClassNames)"
class="inline text-gray-900 animate-spin"
Expand Down
15 changes: 14 additions & 1 deletion spring-boot-admin-server-ui/src/main/frontend/i18n/i18n.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"term": {
"affects_all_instances": "Betrifft alle {count} Instanzen",
"affects_this_instance_only": "Betrifft nur diese Instanz",
"all": "Alle",
"application": "Anwendung",
"attributes": "Attribute",
"bytes": "Bytes",
Expand Down Expand Up @@ -45,6 +46,18 @@
"go_to_previous_page": "Gehe zur vorherigen Seite",
"go_to_page_n": "Gehe zu Seite {page}",
"current_page": "Seite {page}, aktuelle Seite",
"go_to_next_page": "Gehe zur nächsten Seite"
"go_to_next_page": "Gehe zur nächsten Seite",
"no_results_for_term": "Keine Ergebnisse für \"{term}\"."
},
"health": {
"label": "Zustand",
"status": {
"DOWN": "down",
"UP": "up",
"RESTRICTED": "eingeschränkt",
"UNKNOWN": "unbekannt",
"OUT_OF_SERVICE": "außer Betrieb",
"OFFLINE": "offline"
}
}
}
15 changes: 14 additions & 1 deletion spring-boot-admin-server-ui/src/main/frontend/i18n/i18n.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"term": {
"affects_all_instances": "Affects all {count} instances",
"affects_this_instance_only": "Affects this instance only",
"all": "all",
"application": "Application",
"applications_tc": "{n} application | {n} applications",
"attributes": "Attributes",
Expand Down Expand Up @@ -53,6 +54,18 @@
"go_to_previous_page": "Go to previous page",
"go_to_page_n": "Go to page {page}",
"current_page": "Page {page}, current page",
"go_to_next_page": "Go to next page"
"go_to_next_page": "Go to next page",
"no_results_for_term": "No results for \"{term}\"."
},
"health": {
"label": "Health status",
"status": {
"DOWN": "down",
"UP": "up",
"RESTRICTED": "restricted",
"UNKNOWN": "unknown",
"OUT_OF_SERVICE": "out of service",
"OFFLINE": "offline"
}
}
}
4 changes: 3 additions & 1 deletion spring-boot-admin-server-ui/src/main/frontend/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export const findApplicationForInstance = (
);
};

type ApplicationStoreListener = () => void;
type NoopListener = () => void;
type ApplicationAddedListener = (newApplications: Application[]) => void;
type ApplicationStoreListener = NoopListener | ApplicationAddedListener;

export default class ApplicationStore {
private _listeners: { [p: string]: Array<ApplicationStoreListener> } = {};
Expand Down
166 changes: 127 additions & 39 deletions spring-boot-admin-server-ui/src/main/frontend/views/wallboard/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,36 @@

<template>
<section class="wallboard section">
<div class="flex gap-2 justify-end absolute w-full md:w-[28rem] top-14 right-0 bg-black/20 py-3 px-4 rounded-bl">
<sba-input
class="flex-1"
v-model="termFilter"
:placeholder="$t('term.filter')"
name="filter"
type="search"
>
<template #prepend>
<font-awesome-icon icon="filter"/>
</template>
</sba-input>

<select
v-if="healthStatus.length > 1"
v-model="statusFilter"
class="relative focus:z-10 focus:ring-indigo-500 focus:border-indigo-500 block sm:text-sm border-gray-300 rounded"
>
<option selected value="none" v-text="$t('term.all')"/>
<optgroup :label="t('health.label')">
<option
v-for="status in healthStatus"
:key="status"
:value="status"
v-text="t('health.status.' + status)"
/>
</optgroup>
</select>
</div>

<sba-alert
v-if="error"
:error="error"
Expand All @@ -24,57 +54,115 @@
severity="WARN"
/>

<p
<sba-loading-spinner
v-if="!applicationsInitialized"
class="is-muted is-loading"
v-text="t('applications.loading_applications')"
/>
<hex-mesh
v-if="applicationsInitialized"
:class-for-item="classForApplication"
:items="applications"
@click="select"
>
<template #item="{ item: application }">
<div :key="application.name" class="hex__body application">
<div class="application__status-indicator" />
<div class="application__header application__time-ago is-muted">
<sba-time-ago :date="application.statusTimestamp" />
</div>
<div class="application__body">
<h1 class="application__name" v-text="application.name" />
<p
class="application__instances is-muted"
v-text="
t('wallboard.instances_count', application.instances.length)
"

<template v-if="applicationsInitialized">
<div class="flex w-full h-full items-center text-center text-white text-xl"
v-if="termFilter.length > 0 && applications.length === 0"
v-text="t('term.no_results_for_term', {
term: termFilter
})"/>
<hex-mesh
v-if="applicationsInitialized"
:class-for-item="classForApplication"
:items="applications"
@click="select"
>
<template #item="{ item: application }">
<div :key="application.name" class="hex__body application">
<div class="application__status-indicator"/>
<div class="application__header application__time-ago is-muted">
<sba-time-ago :date="application.statusTimestamp"/>
</div>
<div class="application__body">
<h1 class="application__name" v-text="application.name"/>
<p
class="application__instances is-muted"
v-text="
t('wallboard.instances_count', application.instances.length)
"
/>
</div>
<h2
class="application__footer application__version"
v-text="application.buildVersion"
/>
</div>
<h2
class="application__footer application__version"
v-text="application.buildVersion"
/>
</div>
</template>
</hex-mesh>
</template>
</hex-mesh>
</template>
</section>
</template>

<script>
import { useI18n } from 'vue-i18n';
import Fuse from 'fuse.js';
import {computed, ref} from 'vue';
import {useI18n} from 'vue-i18n';
import { HealthStatus } from '@/HealthStatus';
import { useApplicationStore } from '@/composables/useApplicationStore';
import {HealthStatus} from '@/HealthStatus';
import {useApplicationStore} from '@/composables/useApplicationStore';
import hexMesh from '@/views/wallboard/hex-mesh';
export default {
components: { hexMesh },
components: {hexMesh},
setup() {
const { t } = useI18n();
const {t} = useI18n();
const termFilter = ref('');
const statusFilter = ref('none');
const { applications, applicationsInitialized, error } =
const {applications, applicationsInitialized, error} =
useApplicationStore();
return { applications, applicationsInitialized, error, t };
const fuse = computed(
() =>
new Fuse(applications.value, {
includeScore: true,
useExtendedSearch: true,
threshold: 0.25,
keys: ['name', 'buildVersion', 'instances.name', 'instances.id'],
})
);
const filteredApplications = computed(() => {
function filterByTerm() {
if (termFilter.value.length > 0) {
return fuse.value.search(termFilter.value).map((sr) => sr.item);
} else {
return applications.value;
}
}
function filterByStatus(result) {
if (statusFilter.value !== 'none') {
return result.filter(
(application) => application.status === statusFilter.value
);
}
return result;
}
let result = filterByTerm();
result = filterByStatus(result);
return result;
});
const healthStatus = computed(() => {
return applications.value.map((application) => application.status);
});
return {
applications: filteredApplications,
applicationsInitialized,
error,
t,
termFilter,
statusFilter,
healthStatus,
};
},
methods: {
classForApplication(application) {
Expand Down Expand Up @@ -105,17 +193,17 @@ export default {
if (application.instances.length === 1) {
this.$router.push({
name: 'instances/details',
params: { instanceId: application.instances[0].id },
params: {instanceId: application.instances[0].id},
});
} else {
this.$router.push({
name: 'applications',
params: { selected: application.name },
params: {selected: application.name},
});
}
},
},
install({ viewRegistry }) {
install({viewRegistry}) {
viewRegistry.addView({
path: '/wallboard',
name: 'wallboard',
Expand Down

0 comments on commit 6dae843

Please sign in to comment.