mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-23 16:56:50 +08:00
This feature allows admins to find what they are looking for in the admin interface via a search modal. This replaces the admin sidebar filter as the focus of the Ctrl+/ command, but the sidebar filter can also still be used. Perhaps at some point we may remove it or change the shortcut. The search modal presents the following data for filtering: * A list of all admin pages, the same as the sidebar, except also showing "third level" pages like "Email > Skipped" * All site settings * Themes * Components * Reports Admins can also filter which types of items are shown in the modal, for example hiding Settings if they know they are looking for a Page. In this PR, I also have the following fixes: * Site setting filters now clear when moving between filtered site setting pages, previously it was super sticky from Ember * Many translations were moved around, instead of being in various namespaces for the sidebar links and the admin page titles and descriptions, now everything is under `admin.config` namespace, this makes it way easier to reuse this text for pages, search, and sidebar, and if you change it in one place then it is changed everywhere. --------- Co-authored-by: Ella <ella.estigoy@gmail.com>
124 lines
3.5 KiB
Text
Vendored
124 lines
3.5 KiB
Text
Vendored
import Component from "@glimmer/component";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { on } from "@ember/modifier";
|
|
import { action } from "@ember/object";
|
|
import { service } from "@ember/service";
|
|
import { htmlSafe } from "@ember/template";
|
|
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
|
import DButton from "discourse/components/d-button";
|
|
import icon from "discourse/helpers/d-icon";
|
|
import discourseDebounce from "discourse/lib/debounce";
|
|
import { INPUT_DELAY } from "discourse/lib/environment";
|
|
import autoFocus from "discourse/modifiers/auto-focus";
|
|
import AdminSearchFilters from "admin/components/admin-search-filters";
|
|
import { RESULT_TYPES } from "admin/services/admin-search-data-source";
|
|
|
|
export default class AdminSearch extends Component {
|
|
@service adminSearchDataSource;
|
|
|
|
@tracked filter = "";
|
|
@tracked searchResults = [];
|
|
@tracked showFilters = false;
|
|
@tracked loading = false;
|
|
typeFilters = new TrackedObject({
|
|
page: true,
|
|
setting: true,
|
|
theme: true,
|
|
component: true,
|
|
report: true,
|
|
});
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
this.adminSearchDataSource.buildMap();
|
|
}
|
|
|
|
get visibleTypes() {
|
|
return Object.keys(this.typeFilters).filter(
|
|
(type) => this.typeFilters[type]
|
|
);
|
|
}
|
|
|
|
get showLoadingSpinner() {
|
|
return !this.adminSearchDataSource.isLoaded || this.loading;
|
|
}
|
|
|
|
@action
|
|
toggleFilters() {
|
|
this.showFilters = !this.showFilters;
|
|
}
|
|
|
|
@action
|
|
toggleTypeFilter(type) {
|
|
this.typeFilters[type] = !this.typeFilters[type];
|
|
this.search();
|
|
}
|
|
|
|
@action
|
|
changeSearchTerm(event) {
|
|
this.searchResults = [];
|
|
this.filter = event.target.value;
|
|
this.loading = true;
|
|
this.search();
|
|
}
|
|
|
|
@action
|
|
search() {
|
|
discourseDebounce(this, this.#search, INPUT_DELAY);
|
|
}
|
|
|
|
#search() {
|
|
this.searchResults = this.adminSearchDataSource.search(this.filter, {
|
|
types: this.visibleTypes,
|
|
});
|
|
this.loading = false;
|
|
}
|
|
|
|
<template>
|
|
<div class="admin-search__input-container">
|
|
<div class="admin-search__input-group">
|
|
{{icon "magnifying-glass" class="admin-search__input-icon"}}
|
|
<input
|
|
type="text"
|
|
class="admin-search__input-field"
|
|
{{autoFocus}}
|
|
{{on "input" this.changeSearchTerm}}
|
|
/>
|
|
</div>
|
|
<DButton class="btn-flat" @icon="filter" @action={{this.toggleFilters}} />
|
|
</div>
|
|
|
|
{{#if this.showFilters}}
|
|
<AdminSearchFilters
|
|
@toggleTypeFilter={{this.toggleTypeFilter}}
|
|
@typeFilters={{this.typeFilters}}
|
|
@types={{RESULT_TYPES}}
|
|
/>
|
|
{{/if}}
|
|
|
|
<div class="admin-search__results">
|
|
<ConditionalLoadingSpinner @condition={{this.showLoadingSpinner}}>
|
|
{{#each this.searchResults as |result|}}
|
|
<div class="admin-search__result">
|
|
<a href={{result.url}}>
|
|
<div class="admin-search__result-name">
|
|
{{#if result.icon}}
|
|
{{icon result.icon}}
|
|
{{/if}}
|
|
<span
|
|
class="admin-search__result-name-label"
|
|
>{{result.label}}</span>
|
|
</div>
|
|
{{#if result.description}}
|
|
<div class="admin-search__result-description">{{htmlSafe
|
|
result.description
|
|
}}</div>
|
|
{{/if}}
|
|
</a>
|
|
</div>
|
|
{{/each}}
|
|
</ConditionalLoadingSpinner>
|
|
</div>
|
|
</template>
|
|
}
|