mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 22:43:30 +03:00
Added dynamic value selection UI for filter dropdown
refs https://github.com/TryGhost/Team/issues/943 The filter UI behind labs in Admin allows filtering members list across several filters. Since each filter type can have its own specific set of values to choose from, this change adds custom UI based on filter type to select filter value.
This commit is contained in:
parent
e224c96bba
commit
0aa7aca560
@ -44,10 +44,9 @@
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
<GhTextInput
|
||||
@name="filter-value-1"
|
||||
@value={{filter.value}}
|
||||
@input={{fn this.setFilterValue filter.id}}
|
||||
<GhMembersFilterValueLabs
|
||||
@filter={{filter}}
|
||||
@setFilterValue={{this.setFilterValue}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -9,7 +9,7 @@ const FILTER_PROPERTIES = [
|
||||
{label: 'Name', name: 'name', group: 'Basic'},
|
||||
{label: 'Email', name: 'email', group: 'Basic'},
|
||||
// {label: 'Location', name: 'location', group: 'Basic'},
|
||||
{label: 'Newsletter subscription status', name: 'subscribed', group: 'Basic'},
|
||||
{label: 'Newsletter subscription', name: 'subscribed', group: 'Basic'},
|
||||
{label: 'Label', name: 'label', group: 'Basic'},
|
||||
|
||||
// Member subscription
|
||||
@ -71,9 +71,15 @@ export default class GhMembersFilterLabsComponent extends Component {
|
||||
generateNqlFilter(filters) {
|
||||
let query = '';
|
||||
filters.forEach((filter) => {
|
||||
const relationStr = filter.relation === 'is-not' ? '-' : '';
|
||||
const filterValue = filter.value.includes(' ') ? `'${filter.value}'` : filter.value;
|
||||
query += `${filter.type}:${relationStr}${filterValue}+`;
|
||||
if (filter.type === 'label') {
|
||||
const relationStr = filter.relation === 'is-not' ? '-' : '';
|
||||
const filterValue = '[' + filter.value.join(',') + ']';
|
||||
query += `${filter.type}:${relationStr}${filterValue}+`;
|
||||
} else {
|
||||
const relationStr = filter.relation === 'is-not' ? '-' : '';
|
||||
const filterValue = filter.value.includes(' ') ? `'${filter.value}'` : filter.value;
|
||||
query += `${filter.type}:${relationStr}${filterValue}+`;
|
||||
}
|
||||
});
|
||||
return query.slice(0, -1);
|
||||
}
|
||||
@ -88,6 +94,7 @@ export default class GhMembersFilterLabsComponent extends Component {
|
||||
setFilterType(filterId, newType) {
|
||||
const filterToEdit = this.filters.findBy('id', filterId);
|
||||
filterToEdit.set('type', newType);
|
||||
filterToEdit.set('value', '');
|
||||
}
|
||||
|
||||
@action
|
||||
@ -97,9 +104,13 @@ export default class GhMembersFilterLabsComponent extends Component {
|
||||
}
|
||||
|
||||
@action
|
||||
setFilterValue(filterId, event) {
|
||||
setFilterValue(filterType, filterId, filterValue) {
|
||||
const filterToEdit = this.filters.findBy('id', filterId);
|
||||
filterToEdit.set('value', event.target.value);
|
||||
if (filterType === 'label') {
|
||||
filterToEdit.set('value', filterValue);
|
||||
} else {
|
||||
filterToEdit.set('value', filterValue);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
|
85
ghost/admin/app/components/gh-members-filter-value-labs.hbs
Normal file
85
ghost/admin/app/components/gh-members-filter-value-labs.hbs
Normal file
@ -0,0 +1,85 @@
|
||||
{{#if (eq @filter.type 'label')}}
|
||||
<GhMemberLabelInput
|
||||
@onChange={{fn this.setLabelsFilterValue @filter.type @filter.id}}
|
||||
@triggerId="label-input"
|
||||
data-test-input=""
|
||||
/>
|
||||
|
||||
{{else if (eq @filter.type 'status')}}
|
||||
<span class="gh-select">
|
||||
<OneWaySelect
|
||||
@value={{filter.value}}
|
||||
@options={{this.availableFilterOptions.status}}
|
||||
@optionValuePath="name"
|
||||
@optionLabelPath="label"
|
||||
@optionTargetPath="name"
|
||||
@update={{fn this.setFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
|
||||
{{else if (eq @filter.type 'email_count')}}
|
||||
<GhTextInput
|
||||
@value={{@filter.value}}
|
||||
@type="number"
|
||||
@input={{fn this.setInputFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
|
||||
{{else if (eq @filter.type 'email_opened_count')}}
|
||||
<GhTextInput
|
||||
@value={{@filter.value}}
|
||||
@type="number"
|
||||
@input={{fn this.setInputFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
|
||||
{{else if (eq @filter.type 'email_open_rate')}}
|
||||
<GhTextInput
|
||||
@value={{@filter.value}}
|
||||
@type="number"
|
||||
@input={{fn this.setInputFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
|
||||
{{else if (eq @filter.type 'subscriptions.plan_interval')}}
|
||||
<span class="gh-select">
|
||||
<OneWaySelect
|
||||
@value={{filter.value}}
|
||||
@options={{this.availableFilterOptions.subscriptionPriceInterval}}
|
||||
@optionValuePath="name"
|
||||
@optionLabelPath="label"
|
||||
@optionTargetPath="name"
|
||||
@update={{fn this.setFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
|
||||
{{else if (eq @filter.type 'subscriptions.status')}}
|
||||
<span class="gh-select">
|
||||
<OneWaySelect
|
||||
@value={{filter.value}}
|
||||
@options={{this.availableFilterOptions.subscriptionStripeStatus}}
|
||||
@optionValuePath="name"
|
||||
@optionLabelPath="label"
|
||||
@optionTargetPath="name"
|
||||
@update={{fn this.setFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
{{else if (eq @filter.type 'subscribed')}}
|
||||
<span class="gh-select">
|
||||
<OneWaySelect
|
||||
@value={{filter.value}}
|
||||
@options={{this.availableFilterOptions.subscribed}}
|
||||
@optionValuePath="name"
|
||||
@optionLabelPath="label"
|
||||
@optionTargetPath="name"
|
||||
@update={{fn this.setFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
{{else}}
|
||||
<GhTextInput
|
||||
@name={{@filter.id}}
|
||||
@value={{@filter.value}}
|
||||
@input={{fn this.setInputFilterValue @filter.type @filter.id}}
|
||||
/>
|
||||
{{/if}}
|
49
ghost/admin/app/components/gh-members-filter-value-labs.js
Normal file
49
ghost/admin/app/components/gh-members-filter-value-labs.js
Normal file
@ -0,0 +1,49 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
const FILTER_OPTIONS = {
|
||||
subscriptionPriceInterval: [
|
||||
{label: 'Monthly', name: 'month'},
|
||||
{label: 'Yearly', name: 'year'}
|
||||
],
|
||||
status: [
|
||||
{label: 'Paid', name: 'paid'},
|
||||
{label: 'Free', name: 'free'},
|
||||
{label: 'Complimentary', name: 'comped'}
|
||||
],
|
||||
subscribed: [
|
||||
{label: 'Subscribed', name: 'true'},
|
||||
{label: 'Unsubscribed', name: 'false'}
|
||||
],
|
||||
subscriptionStripeStatus: [
|
||||
{label: 'Active', name: 'active'},
|
||||
{label: 'Trialing', name: 'trialing'},
|
||||
{label: 'Canceled', name: 'canceled'},
|
||||
{label: 'Unpaid', name: 'unpaid'},
|
||||
{label: 'Past Due', name: 'past_due'},
|
||||
{label: 'Incomplete', name: 'incomplete'},
|
||||
{label: 'Incomplete - Expired', name: 'incomplete_expired'}
|
||||
]
|
||||
};
|
||||
|
||||
export default class GhMembersFilterValueLabs extends Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.availableFilterOptions = FILTER_OPTIONS;
|
||||
}
|
||||
|
||||
@action
|
||||
setInputFilterValue(filterType, filterId, event) {
|
||||
this.args.setFilterValue(filterType, filterId, event.target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
setLabelsFilterValue(filterType, filterId, labels) {
|
||||
this.args.setFilterValue(filterType, filterId, labels.map(label => label.slug));
|
||||
}
|
||||
|
||||
@action
|
||||
setFilterValue(filterType, filterId, value) {
|
||||
this.args.setFilterValue(filterType, filterId, value);
|
||||
}
|
||||
}
|
@ -2,9 +2,8 @@
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
<span class="midlightgrey">{{labels}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq @filterColumn 'status')}}
|
||||
{{else if (eq @filterColumn 'status')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.status))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{capitalize @member.status}}</span>
|
||||
@ -12,9 +11,8 @@
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq @filterColumn 'email_count')}}
|
||||
{{else if (eq @filterColumn 'email_count')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.emailCount))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{@member.emailCount}}</span>
|
||||
@ -22,9 +20,8 @@
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq @filterColumn 'email_opened_count')}}
|
||||
{{else if (eq @filterColumn 'email_opened_count')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.emailOpenedCount))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenedCount}}</span>
|
||||
@ -32,9 +29,8 @@
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq @filterColumn 'subscribed')}}
|
||||
{{else if (eq @filterColumn 'subscribed')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.subscribed))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{@member.subscribed}}</span>
|
||||
@ -42,4 +38,20 @@
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{else if (eq @filterColumn 'subscriptions.status')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.subscriptions?.[0]?.status))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{@member.subscriptions?.[0]?.status}}</span>
|
||||
{{else}}
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{else if (eq @filterColumn 'subscriptions.plan_interval')}}
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
|
||||
{{#if (not (is-empty @member.subscriptions?.[0]?.plan?.interval))}}
|
||||
<span class="gh-members-list-open-rate-mobile">{{@member.subscriptions?.[0]?.plan?.interval}}</span>
|
||||
{{else}}
|
||||
<span class="midlightgrey">-</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
@ -35,8 +35,8 @@
|
||||
grid-column-gap: 8px;
|
||||
}
|
||||
|
||||
.gh-filter-builder .gh-input,
|
||||
.gh-filter-builder .gh-select,
|
||||
.gh-filter-builder .gh-input,
|
||||
.gh-filter-builder .gh-select,
|
||||
.gh-filter-builder select {
|
||||
height: 33px;
|
||||
font-size: 1.35rem;
|
||||
@ -108,4 +108,12 @@
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.gh-filter-block .ember-power-select-multiple-trigger {
|
||||
height: 33px !important;
|
||||
}
|
||||
|
||||
.gh-filter-block .label-token {
|
||||
margin: 2px !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user