mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-27 22:49:56 -05:00
Fixed members table filters and URL handling in admin
fixes https://github.com/TryGhost/Team/issues/1344 fixes https://github.com/TryGhost/Team/issues/1127 This fixes a couple of bugs with the filter menu on the members page in admin: - When opening the members page, the filters property was passed back from the filter component to the members controller. This caused a bug that the filter columns where not visible on reload. - Fixed handling invalid filter parameters - When updating the URL, the members page now properly reloads - Fixed a bug that 'falsy' values in the NQL filter were removed on reload: - Filtering on unsubscribed members was gone after a page reload - Filtering on 0 emails was gone after a page reload - This is fixed by converting numbers and booleans to strings after parsing the NQL-filter - Fixed a bug where boolean values didn't match any value in the select menu, causing the default option to be visible - Filtering members by 'unsubscribed' -> parsed as false (boolean) -> select menu opened -> false value (boolean) didn't match 'false' (string) so the first option was shown instead (subscribed). - This is also fixed by converting numbers and booleans to strings after parsing the NQL-filter The way this is currently handled is not great. The parsing happens in the filter component, but should happen on a different layer, maybe in a different helper. This is tracked here: https://github.com/TryGhost/Team/issues/1849
This commit is contained in:
parent
403d24a01b
commit
9dbb2785bb
4 changed files with 70 additions and 5 deletions
|
@ -4,7 +4,7 @@
|
|||
class="gh-btn gh-btn-icon gh-btn-action-icon"
|
||||
data-test-button="members-filter-actions"
|
||||
>
|
||||
<span class="{{if @isFiltered "gh-btn-label-green"}}">
|
||||
<span class="{{if @isFiltered "gh-btn-label-green"}}" {{did-update this.parseDefaultFilters @parseFilterParamCounter}}>
|
||||
{{svg-jar "filter"}}
|
||||
Filter
|
||||
{{#if @isFiltered}}
|
||||
|
|
|
@ -190,11 +190,27 @@ export default class MembersFilter extends Component {
|
|||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.parseDefaultFilters();
|
||||
this.fetchTiers.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is not super clean as it uses did-update, but for now this is required to make URL changes work
|
||||
* properly.
|
||||
* Problem: filter parameter is changed in the members controller by modifying the URL directly
|
||||
* -> the filters property is not updated in the members controller because the new parameter is not parsed again
|
||||
* -> we need to listen for changes in the property and parse it again
|
||||
* -> better future proof solution: move the filter parsing logic elsewhere so it can be parsed in the members controller
|
||||
*/
|
||||
@action
|
||||
parseDefaultFilters() {
|
||||
if (this.args.defaultFilterParam) {
|
||||
this.parseNqlFilter(this.args.defaultFilterParam);
|
||||
}
|
||||
|
||||
this.fetchTiers.perform();
|
||||
// Pass the parsed filter to the parent component
|
||||
// this doesn't start a new network request, and doesn't update filterParam again
|
||||
this.applyParsedFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -329,6 +345,12 @@ export default class MembersFilter extends Component {
|
|||
value = nqlValue;
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean' || typeof value === 'number') {
|
||||
// Transform it to a string, to keep it compatible with the internally used value in admin
|
||||
// + make sure false and 0 are truthy
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
if (relation && value) {
|
||||
return new Filter({
|
||||
type: key,
|
||||
|
@ -342,14 +364,25 @@ export default class MembersFilter extends Component {
|
|||
|
||||
parseNqlFilter(filterParam) {
|
||||
const validKeys = Object.keys(FILTER_RELATIONS_OPTIONS);
|
||||
const filters = nql.parse(filterParam);
|
||||
let filters;
|
||||
|
||||
try {
|
||||
filters = nql.parse(filterParam);
|
||||
} catch (e) {
|
||||
// Invalid nql filter
|
||||
this.filters = new TrackedArray([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const filterKeys = Object.keys(filters);
|
||||
|
||||
let filterData = [];
|
||||
|
||||
if (filterKeys?.length === 1 && validKeys.includes(filterKeys[0])) {
|
||||
const filterObj = this.parseNqlFilterKey(filters);
|
||||
filterData = [filterObj];
|
||||
if (filterObj) {
|
||||
filterData = [filterObj];
|
||||
}
|
||||
} else if (filters?.$and) {
|
||||
const andFilters = filters?.$and || [];
|
||||
filterData = andFilters.filter((nqlFilter) => {
|
||||
|
@ -485,6 +518,18 @@ export default class MembersFilter extends Component {
|
|||
this.args.onApplyFilter(query, validFilters);
|
||||
}
|
||||
|
||||
@action
|
||||
applyParsedFilter() {
|
||||
const validFilters = this.filters.filter((filter) => {
|
||||
if (['label', 'tier'].includes(filter.type)) {
|
||||
return filter.value?.length;
|
||||
}
|
||||
return filter.value;
|
||||
});
|
||||
|
||||
this.args.onApplyParsedFilter(validFilters);
|
||||
}
|
||||
|
||||
@action
|
||||
resetFilter() {
|
||||
const filters = [];
|
||||
|
|
|
@ -61,6 +61,8 @@ export default class MembersController extends Controller {
|
|||
|
||||
@tracked _availableLabels = A([]);
|
||||
|
||||
@tracked parseFilterParamCounter = 0;
|
||||
|
||||
paidParams = PAID_PARAMS;
|
||||
|
||||
constructor() {
|
||||
|
@ -243,6 +245,15 @@ export default class MembersController extends Controller {
|
|||
this.filters = filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to set the filters after the url filterParam has been parsed again
|
||||
*/
|
||||
@action
|
||||
applyParsedFilter(filters) {
|
||||
this.softFilters = A([]);
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
@action
|
||||
applySoftFilter(filterStr, filters) {
|
||||
this.softFilters = filters;
|
||||
|
@ -432,6 +443,13 @@ export default class MembersController extends Controller {
|
|||
this.filters = A([]);
|
||||
this.softFilterParam = null;
|
||||
this.softFilters = A([]);
|
||||
} else {
|
||||
this.filterParam = params.filterParam;
|
||||
|
||||
// Trigger a did-update call in the filter component, so we get freshly parsed filters
|
||||
// This is temporary, and a ugly pattern, but essential to make it work for now, until we moved the filter parsing logic
|
||||
// out of the component
|
||||
this.parseFilterParamCounter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,9 +27,11 @@
|
|||
@onApplyFilter={{this.applyFilter}}
|
||||
@defaultFilterParam={{this.filterParam}}
|
||||
@onApplySoftFilter={{this.applySoftFilter}}
|
||||
@onApplyParsedFilter={{this.applyParsedFilter}}
|
||||
@onResetSoftFilter={{this.resetSoftFilter}}
|
||||
@onResetFilter={{this.resetFilter}}
|
||||
@onLabelEdit={{this.editLabel}}
|
||||
@parseFilterParamCounter={{this.parseFilterParamCounter}}
|
||||
/>
|
||||
<span class="dropdown">
|
||||
<GhDropdownButton
|
||||
|
|
Loading…
Add table
Reference in a new issue