Switched "Last seen" filter to standard date picker filter

refs https://github.com/TryGhost/Team/issues/1410

- we have problems translating an "x days ago" NQL filter back to the UI component so as a temporary measure we're switching to a datepicker input as we have a working solution for that
- a later iteration will add shortcut buttons for selecting typical "x days ago" dates in the datepicker
This commit is contained in:
Kevin Ansfield 2022-03-04 13:55:25 +00:00
parent e23ae31e8f
commit 1f062ff844
3 changed files with 59 additions and 100 deletions

View File

@ -33,19 +33,13 @@
</span>
{{else if (eq @filter.type 'last_seen_at')}}
<div class="relative">
<span class="gh-input-percentage-label">days ago</span>
<input
type="number"
value={{@filter.value}}
class="gh-input"
aria-label="Number of days ago"
{{on "input" (fn this.setInputFilterValue @filter.type @filter.id)}}
{{on "blur" (fn this.updateInputFilterValue @filter.type @filter.id)}}
{{on "keypress" (fn this.updateInputFilterValueOnEnter @filter.type @filter.id)}}
data-test-input="members-filter-value"
/>
</div>
<GhDatePicker
@value={{@filter.value}}
@maxDate={{now}}
@maxDateError="Must be in the past"
@onChange={{fn @setFilterValue @filter.type @filter.id}}
data-test-input="members-filter-value"
/>
{{else if (eq @filter.type 'created_at')}}
<GhDatePicker

View File

@ -15,7 +15,7 @@ const FILTER_PROPERTIES = [
{label: 'Label', name: 'label', group: 'Basic'},
{label: 'Tiers', name: 'product', group: 'Basic', feature: 'multipleProducts'},
{label: 'Newsletter subscription', name: 'subscribed', group: 'Basic'},
{label: 'Last seen', name: 'last_seen_at', group: 'Basic', feature: 'membersLastSeenFilter'},
{label: 'Last seen', name: 'last_seen_at', group: 'Basic', valueType: 'date', feature: 'membersLastSeenFilter'},
{label: 'Created', name: 'created_at', group: 'Basic', valueType: 'date'},
// Member subscription
@ -38,6 +38,17 @@ const FILTER_PROPERTIES = [
// {label: 'Open rate (60 days)', name: 'x', group: 'Email'},
];
const DATE_RELATION_OPTIONS = [
{label: 'before', name: 'is-less'},
{label: 'on or before', name: 'is-or-less'},
// TODO: these cause problems because they require multiple NQL statements, eg:
// created_at:>='2022-03-02 00:00'+created_at:<'2022-03-03 00:00'
// {label: 'on', name: 'is'},
// {label: 'not on', name: 'is-not'},
{label: 'after', name: 'is-greater'},
{label: 'on or after', name: 'is-or-greater'}
];
const FILTER_RELATIONS_OPTIONS = {
// name: [
// {label: 'is', name: 'is'},
@ -59,20 +70,8 @@ const FILTER_RELATIONS_OPTIONS = {
{label: 'is', name: 'is'},
{label: 'is not', name: 'is-not'}
],
last_seen_at: [
{label: 'less than', name: 'is-less'},
{label: 'more than', name: 'is-greater'}
],
created_at: [
{label: 'before', name: 'is-less'},
{label: 'on or before', name: 'is-or-less'},
// TODO: these cause problems because they require multiple NQL statements, eg:
// created_at:>='2022-03-02 00:00'+created_at:<'2022-03-03 00:00'
// {label: 'on', name: 'is'},
// {label: 'not on', name: 'is-not'},
{label: 'after', name: 'is-greater'},
{label: 'on or after', name: 'is-or-greater'}
],
last_seen_at: DATE_RELATION_OPTIONS,
created_at: DATE_RELATION_OPTIONS,
status: [
{label: 'is', name: 'is'},
{label: 'is not', name: 'is-not'}
@ -85,26 +84,8 @@ const FILTER_RELATIONS_OPTIONS = {
{label: 'is', name: 'is'},
{label: 'is not', name: 'is-not'}
],
'subscriptions.start_date': [
{label: 'before', name: 'is-less'},
{label: 'on or before', name: 'is-or-less'},
// TODO: these cause problems because they require multiple NQL statements, eg:
// created_at:>='2022-03-02 00:00'+created_at:<'2022-03-03 00:00'
// {label: 'on', name: 'is'},
// {label: 'not on', name: 'is-not'},
{label: 'after', name: 'is-greater'},
{label: 'on or after', name: 'is-or-greater'}
],
'subscriptions.current_period_end': [
{label: 'before', name: 'is-less'},
{label: 'on or before', name: 'is-or-less'},
// TODO: these cause problems because they require multiple NQL statements, eg:
// created_at:>='2022-03-02 00:00'+created_at:<'2022-03-03 00:00'
// {label: 'on', name: 'is'},
// {label: 'not on', name: 'is-not'},
{label: 'after', name: 'is-greater'},
{label: 'on or after', name: 'is-or-greater'}
],
'subscriptions.start_date': DATE_RELATION_OPTIONS,
'subscriptions.current_period_end': DATE_RELATION_OPTIONS,
email_count: [
{label: 'is', name: 'is'},
{label: 'is greater than', name: 'is-greater'},
@ -276,13 +257,6 @@ export default class MembersFilter extends Component {
const relationStr = filter.relation === 'is-not' ? '-' : '';
const filterValue = '[' + filter.value.join(',') + ']';
query += `${filter.type}:${relationStr}${filterValue}+`;
} else if (filter.type === 'last_seen_at') {
// is-greater = more than x days ago = <date
// is-less = less than x days ago = >date
const relationStr = filter.relation === 'is-greater' ? '<=' : '>=';
const daysAgoMoment = moment.utc().subtract(filter.value, 'days');
const filterValue = `'${daysAgoMoment.format(nqlDateFormat)}'`;
query += `${filter.type}:${relationStr}${filterValue}+`;
} else if (filterProperty.valueType === 'date') {
const operator = relationMap[filter.relation];
let relationStr;

View File

@ -536,37 +536,41 @@ describe('Acceptance: Members filtering', function () {
it('can filter by last seen date', async function () {
clock = sinon.useFakeTimers({
now: moment('2022-02-10 11:50:00.000Z').toDate(),
now: moment('2022-02-05 11:50:00.000Z').toDate(),
shouldAdvanceTime: true
});
// add some members to filter
this.server.createList('member', 3, {lastSeenAt: moment('2022-02-01 12:00:00').format('YYYY-MM-DD HH:mm:ss')});
this.server.createList('member', 4, {lastSeenAt: moment('2022-02-05 12:00:00').format('YYYY-MM-DD HH:mm:ss')});
this.server.createList('member', 3, {lastSeenAt: moment('2022-02-01 11:00:00').format('YYYY-MM-DD HH:mm:ss')});
this.server.createList('member', 4, {lastSeenAt: moment('2022-02-05 11:00:00').format('YYYY-MM-DD HH:mm:ss')});
await visit('/members');
expect(findAll('[data-test-list="members-list-item"]').length, '# of initial member rows')
.to.equal(7);
const filterSelect = `[data-test-members-filter="0"]`;
const typeSelect = `${filterSelect} [data-test-select="members-filter"]`;
const operatorSelect = `${filterSelect} [data-test-select="members-filter-operator"]`;
const valueInput = `${filterSelect} [data-test-input="members-filter-value"] [data-test-date-picker-input]`;
const valueDatePicker = `${filterSelect} [data-test-input="members-filter-value"]`;
await click('[data-test-button="members-filter-actions"]');
const filterSelector = `[data-test-members-filter="0"]`;
await fillIn(`${filterSelector} [data-test-select="members-filter"]`, 'last_seen_at');
const operatorSelector = `${filterSelector} [data-test-select="members-filter-operator"]`;
await fillIn(typeSelect, 'last_seen_at');
// has the right operators
const operatorOptions = findAll(`${operatorSelector} option`);
expect(operatorOptions).to.have.length(2);
const operatorOptions = findAll(`${operatorSelect} option`);
expect(operatorOptions).to.have.length(4);
expect(operatorOptions[0]).to.have.value('is-less');
expect(operatorOptions[1]).to.have.value('is-greater');
expect(operatorOptions[1]).to.have.value('is-or-less');
expect(operatorOptions[2]).to.have.value('is-greater');
expect(operatorOptions[3]).to.have.value('is-or-greater');
const valueInput = `${filterSelector} [data-test-input="members-filter-value"]`;
// has the right default operator
expect(find(operatorSelect)).to.have.value('is-or-less');
// has no default filter
expect(find(valueInput)).to.have.value('');
// has expected default value
expect(find(valueInput)).to.have.value('2022-02-05');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - default')
.to.equal(7);
@ -576,42 +580,29 @@ describe('Acceptance: Members filtering', function () {
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - after blur')
.to.equal(7);
// can change filter
await fillIn(valueInput, '2'); // last seen less than 2 days ago
await blur(valueInput);
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen less than 2 days ago')
.to.equal(0);
// can change operator
await fillIn(operatorSelect, 'is-less');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - is before 2022-02-05')
.to.equal(3);
await fillIn(valueInput, '6'); // last seen less than 6 days ago
// can change filter via input
await fillIn(operatorSelect, 'is-greater');
await fillIn(valueInput, '2022-02-01');
await blur(valueInput);
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen less than 6 days ago')
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - is after 2022-02-01')
.to.equal(4);
// can change filter via date picker
await fillIn(operatorSelect, 'is-or-greater');
await datepickerSelect(valueDatePicker, moment.utc('2022-01-01').toDate());
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - is after 2022-01-01')
.to.equal(7);
// table shows last seen column+data
expect(find('[data-test-table-column="last_seen_at"]')).to.exist;
expect(findAll('[data-test-table-data="last_seen_at"]').length).to.equal(4);
expect(find('[data-test-table-data="last_seen_at"]')).to.contain.text('5 Feb 2022');
expect(find('[data-test-table-data="last_seen_at"]')).to.contain.text('5 days ago');
await fillIn(valueInput, '11'); // last seen less than 11 days ago
await blur(valueInput);
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen less than 11 days ago')
.to.equal(7);
// can change operator
await fillIn(operatorSelector, 'is-greater');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen more than 11 days ago')
.to.equal(0);
await fillIn(valueInput, '6'); // last seen more than 6 days ago
await blur(valueInput);
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen more than 6 days ago')
.to.equal(3);
await fillIn(valueInput, '2'); // last seen more than 2 days ago
await blur(valueInput);
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - last seen more than 2 days ago')
.to.equal(7);
expect(findAll('[data-test-table-data="last_seen_at"]').length).to.equal(7);
expect(find('[data-test-table-data="last_seen_at"]')).to.contain.trimmed.text('1 Feb 2022');
expect(find('[data-test-table-data="last_seen_at"]')).to.contain.trimmed.text('4 days ago');
});
it('can filter by created at date', async function () {