diff --git a/CHANGELOG.md b/CHANGELOG.md index 790a446fa..3081e3b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancements - [VM migration] Display hosts' free memory [#3264](https://github.com/vatesfr/xen-orchestra/issues/3264) (PR [#3832](https://github.com/vatesfr/xen-orchestra/pull/3832)) +- [Plugins] New field to filter displayed plugins (PR [#3832](https://github.com/vatesfr/xen-orchestra/pull/3871)) ### Bug fixes diff --git a/packages/xo-web/src/xo-app/settings/plugins/index.js b/packages/xo-web/src/xo-app/settings/plugins/index.js index 8000a45af..30c7eb6dd 100644 --- a/packages/xo-web/src/xo-app/settings/plugins/index.js +++ b/packages/xo-web/src/xo-app/settings/plugins/index.js @@ -1,12 +1,15 @@ +import * as ComplexMatcher from 'complex-matcher' import _ from 'intl' import ActionButton from 'action-button' import ActionToggle from 'action-toggle' import Button from 'button' import Component from 'base-component' +import decorate from 'apply-decorators' import GenericInput from 'json-schema-input' import Icon from 'icon' import isEmpty from 'lodash/isEmpty' import map from 'lodash/map' +import orderBy from 'lodash/orderBy' import pFinally from 'promise-toolbox/finally' import React from 'react' import size from 'lodash/size' @@ -14,6 +17,7 @@ import { addSubscriptions } from 'utils' import { alert } from 'modal' import { createSelector } from 'reselect' import { generateUiSchema } from 'xo-json-schema-input' +import { injectState, provideState } from 'reaclette' import { Row, Col } from 'grid' import { configurePlugin, @@ -251,29 +255,75 @@ class Plugin extends Component { } } -@addSubscriptions({ - plugins: subscribePlugins, -}) -export default class Plugins extends Component { - render() { - if (isEmpty(this.props.plugins)) { - return ( -

- {_('noPlugins')} -

- ) - } +export default decorate([ + addSubscriptions({ + plugins: subscribePlugins, + }), + provideState({ + effects: { + onSearchChange( + _, + { + target: { value }, + } + ) { + const { location, router } = this.props + router.replace({ + ...location, + query: { + ...location.query, + s: value, + }, + }) + }, + }, + computed: { + search: ( + _, + { + location: { + query: { s = '' }, + }, + } + ) => s, + filteredPlugins: ({ predicate }, { plugins }) => + predicate === undefined ? plugins : plugins.filter(predicate), + predicate: ({ search }) => { + if (search.trim() === '') { + return + } - return ( + try { + return ComplexMatcher.parse(search).createPredicate() + } catch (error) { + console.warn(error) + } + }, + sortedPlugins: ({ filteredPlugins }) => orderBy(filteredPlugins, 'name'), + }, + }), + injectState, + ({ effects, state, plugins }) => + isEmpty(plugins) ? ( +

+ {_('noPlugins')} +

+ ) : (
+

+ +

- ) - } -} + ), +])