From 0fc0bf40f1e53c674e14d10644d6741cd8be79ea Mon Sep 17 00:00:00 2001 From: "Fabien \"egg\" O'Carroll" Date: Mon, 22 Aug 2022 14:24:32 -0400 Subject: [PATCH] Added ability to filter members by attribution to Admin refs https://github.com/TryGhost/Team/issues/1832 --- .../app/components/gh-resource-select.hbs | 14 ++++ .../app/components/gh-resource-select.js | 81 +++++++++++++++++++ .../app/components/members/filter-value.hbs | 16 ++++ .../app/components/members/filter-value.js | 34 ++++++++ ghost/admin/app/components/members/filter.js | 8 +- 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 ghost/admin/app/components/gh-resource-select.hbs create mode 100644 ghost/admin/app/components/gh-resource-select.js diff --git a/ghost/admin/app/components/gh-resource-select.hbs b/ghost/admin/app/components/gh-resource-select.hbs new file mode 100644 index 0000000000..df649aa17d --- /dev/null +++ b/ghost/admin/app/components/gh-resource-select.hbs @@ -0,0 +1,14 @@ + + {{resource.name}} + diff --git a/ghost/admin/app/components/gh-resource-select.js b/ghost/admin/app/components/gh-resource-select.js new file mode 100644 index 0000000000..94e2ac9aa1 --- /dev/null +++ b/ghost/admin/app/components/gh-resource-select.js @@ -0,0 +1,81 @@ +import Component from '@glimmer/component'; +import {action} from '@ember/object'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; +import {tracked} from '@glimmer/tracking'; + +export default class GhResourceSelect extends Component { + @service store; + + @tracked _options = []; + + get renderInPlace() { + return this.args.renderInPlace === undefined ? false : this.args.renderInPlace; + } + + constructor() { + super(...arguments); + this.fetchOptionsTask.perform(); + } + + get options() { + return this._options; + } + + get flatOptions() { + const options = []; + + function getOptions(option) { + if (option.options) { + return option.options.forEach(getOptions); + } + + options.push(option); + } + + this._options.forEach(getOptions); + + return options; + } + + get selectedOptions() { + const resources = this.args.resources || []; + return this.flatOptions.filter(option => resources.find(resource => resource.id === option.id)); + } + + @action + onChange(options) { + this.args.onChange(options); + } + + @task + *fetchOptionsTask() { + const options = yield []; + + const posts = yield this.store.query('post', {filter: 'status:published', limit: 'all'}); + const pages = yield this.store.query('page', {filter: 'status:published', limit: 'all'}); + + function mapResource(resource) { + return { + name: resource.title, + id: resource.id + }; + } + + if (posts.length > 0) { + options.push({ + groupName: 'Posts', + options: posts.map(mapResource) + }); + } + + if (pages.length > 0) { + options.push({ + groupName: 'Pages', + options: pages.map(mapResource) + }); + } + + this._options = options; + } +} diff --git a/ghost/admin/app/components/members/filter-value.hbs b/ghost/admin/app/components/members/filter-value.hbs index 1d3a0265e0..da224ff724 100644 --- a/ghost/admin/app/components/members/filter-value.hbs +++ b/ghost/admin/app/components/members/filter-value.hbs @@ -42,6 +42,22 @@ /> +{{else if (eq @filter.type 'conversion')}} +
+ +
+ +{{else if (eq @filter.type 'signup')}} +
+ +
+ {{else if (eq @filter.type 'subscribed')}} { + return { + id: resource + }; + }); + } + return []; + } + + get conversionAttributionFilterValue() { + if (this.args.filter?.type === 'conversion') { + const resources = this.args.filter?.value || []; + return resources.map((resource) => { + return { + id: resource + }; + }); + } + return []; + } + @action setInputFilterValue(filter, event) { this.filterValue = event.target.value; @@ -78,4 +102,14 @@ export default class MembersFilterValue extends Component { setTiersFilterValue(filter, tiers) { this.args.setFilterValue(filter, tiers.map(tier => tier.slug)); } + + @action + setConversionAttributionFilterValue(filter, resources) { + this.args.setFilterValue(filter, resources.map(resource => resource.id)); + } + + @action + setSignupAttributionFilterValue(filter, resources) { + this.args.setFilterValue(filter, resources.map(resource => resource.id)); + } } diff --git a/ghost/admin/app/components/members/filter.js b/ghost/admin/app/components/members/filter.js index 5dfc2eef93..2706d47ffa 100644 --- a/ghost/admin/app/components/members/filter.js +++ b/ghost/admin/app/components/members/filter.js @@ -28,13 +28,15 @@ const FILTER_PROPERTIES = [ // Emails {label: 'Emails sent (all time)', name: 'email_count', group: 'Email'}, {label: 'Emails opened (all time)', name: 'email_opened_count', group: 'Email'}, - {label: 'Open rate (all time)', name: 'email_open_rate', group: 'Email'} + {label: 'Open rate (all time)', name: 'email_open_rate', group: 'Email'}, // {label: 'Emails sent (30 days)', name: 'x', group: 'Email'}, // {label: 'Emails opened (30 days)', name: 'x', group: 'Email'}, // {label: 'Open rate (30 days)', name: 'x', group: 'Email'}, // {label: 'Emails sent (60 days)', name: 'x', group: 'Email'}, // {label: 'Emails opened (60 days)', name: 'x', group: 'Email'}, // {label: 'Open rate (60 days)', name: 'x', group: 'Email'}, + {label: 'Subscribed from', name: 'signup', group: 'Attribution', valueType: 'array', feature: 'memberAttribution'}, + {label: 'Converted from', name: 'conversion', group: 'Attribution', valueType: 'array', feature: 'memberAttribution'} ]; const MATCH_RELATION_OPTIONS = [ @@ -82,7 +84,9 @@ const FILTER_RELATIONS_OPTIONS = { 'subscriptions.current_period_end': DATE_RELATION_OPTIONS, email_count: NUMBER_RELATION_OPTIONS, email_opened_count: NUMBER_RELATION_OPTIONS, - email_open_rate: NUMBER_RELATION_OPTIONS + email_open_rate: NUMBER_RELATION_OPTIONS, + signup: MATCH_RELATION_OPTIONS, + conversion: MATCH_RELATION_OPTIONS }; const FILTER_VALUE_OPTIONS = {