0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00

Added "Subscription start date" members filter

no issue

- added datepicker based filter for the start date of paid subscriptions
- updated table to add "Start date" column when filtered, showing the paid subscription's start date
This commit is contained in:
Kevin Ansfield 2022-03-03 19:38:28 +00:00
parent d0763531f5
commit ee8b9452a5
4 changed files with 107 additions and 0 deletions

View file

@ -71,4 +71,14 @@
<span class="midlightgrey">-</span>
{{/if}}
</LinkTo>
{{else if (eq @filterColumn 'subscriptions.start_date')}}
<LinkTo @route="member" @model={{@member}} class="gh-list-data middarkgrey f8" data-test-table-data={{@filterColumn}}>
{{#if (not (is-empty @member.subscriptions.firstObject.start_date))}}
{{moment-format (moment-site-tz @member.subscriptions.firstObject.start_date) "D MMM YYYY"}}
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.subscriptions.firstObject.start_date}}</div>
{{else}}
<span class="midlightgrey">-</span>
{{/if}}
</LinkTo>
{{/if}}

View file

@ -136,6 +136,13 @@
{{svg-jar "arrow-down-small"}}
</span>
{{else if (eq @filter.type 'subscriptions.start_date')}}
<GhDatePicker
@value={{@filter.value}}
@onChange={{fn @setFilterValue @filter.type @filter.id}}
data-test-input="members-filter-value"
/>
{{else}}
<input
type="text"

View file

@ -23,6 +23,7 @@ const FILTER_PROPERTIES = [
// {label: 'Tier', name: 'tier', group: 'Subscription'},
{label: 'Billing period', name: 'subscriptions.plan_interval', group: 'Subscription'},
{label: 'Stripe subscription status', name: 'subscriptions.status', group: 'Subscription'},
{label: 'Start date', name: 'subscriptions.start_date', valueType: 'date', group: 'Subscription', feature: 'membersTimeFilters'},
// Emails
{label: 'Emails sent (all time)', name: 'email_count', group: 'Email'},
@ -83,6 +84,16 @@ 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'}
],
email_count: [
{label: 'is', name: 'is'},
{label: 'is greater than', name: 'is-greater'},

View file

@ -764,6 +764,85 @@ describe('Acceptance: Members filtering', function () {
expect(find(valueInput)).to.have.value('2022-02-28');
});
it('can filter by paid subscription start date', async function () {
clock = sinon.useFakeTimers({
now: moment('2022-03-01 09:00:00.000Z').toDate(),
shouldAdvanceTime: true
});
// add some members to filter
this.server.createList('member', 3, {subscriptions: [{start_date: moment('2022-02-01 12:00:00').format('YYYY-MM-DD HH:mm:ss')}]});
this.server.createList('member', 4, {subscriptions: [{start_date: moment('2022-02-05 12:00:00').format('YYYY-MM-DD HH:mm:ss')}]});
this.server.createList('member', 2, {subscriptions: []});
await visit('/members');
expect(findAll('[data-test-list="members-list-item"]').length, '# of initial member rows')
.to.equal(9);
await click('[data-test-button="members-filter-actions"]');
const filterSelect = `[data-test-members-filter="0"]`;
const typeSelect = `${filterSelect} [data-test-select="members-filter"]`;
const operatorSelect = `${filterSelect} [data-test-select="members-filter-operator"]`;
expect(find(`${filterSelect} [data-test-select="members-filter"] option[value="subscriptions.start_date"]`), 'subscriptions.start_date filter option').to.exist;
await fillIn(typeSelect, 'subscriptions.start_date');
// has the right operators
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-or-less');
// expect(operatorOptions[2]).to.have.value('is');
// expect(operatorOptions[3]).to.have.value('is-not');
expect(operatorOptions[2]).to.have.value('is-greater');
expect(operatorOptions[3]).to.have.value('is-or-greater');
const valueDateInput = `${filterSelect} [data-test-input="members-filter-value"] [data-test-date-picker-input]`;
const valueDatePicker = `${filterSelect} [data-test-input="members-filter-value"]`;
// operator defaults to "on or before"
expect(find(operatorSelect)).to.have.value('is-or-less');
// value defaults to today's date
expect(find(valueDateInput)).to.have.value('2022-03-01');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - default')
.to.equal(7);
// can change date
await datepickerSelect(valueDatePicker, moment.utc('2022-02-03').toDate());
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - 2022-02-03')
.to.equal(3);
// can change operator
await fillIn(operatorSelect, 'is-greater');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - is-greater')
.to.equal(4);
// can populate filter from URL
// TODO: leaving screen is needed, suggests component is not fully reactive and needs to be torn down.
// - see <Members::Filter> constructor
await visit(`/`);
const filter = encodeURIComponent(`subscriptions.start_date:<='2022-02-01 23:59:59'`);
await visit(`/members?filter=${filter}`);
await click('[data-test-button="members-filter-actions"]');
expect(find(typeSelect), 'type select - from URL').to.have.value('subscriptions.start_date');
expect(find(operatorSelect), 'operator select - from URL').to.have.value('is-or-less');
expect(find(valueDateInput), 'date input - from URL').to.have.value('2022-02-01');
expect(findAll('[data-test-list="members-list-item"]').length, '# of filtered member rows - from URL')
.to.equal(3);
// it adds extra column to table
expect(find('[data-test-table-column="subscriptions.start_date"]')).to.exist;
expect(findAll('[data-test-table-data="subscriptions.start_date"]').length).to.equal(3);
expect(find('[data-test-table-data="subscriptions.start_date"]')).to.contain.text('1 Feb 2022');
expect(find('[data-test-table-data="subscriptions.start_date"]')).to.contain.text('a month ago');
});
it('can handle multiple filters', async function () {
// add some members to filter
this.server.createList('member', 1, {subscriptions: [{status: 'active'}]});