From e7eacf6cdfb95e4a8a42f320d27c0122b4ef8d74 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Wed, 21 Aug 2024 15:17:17 +0700 Subject: [PATCH 1/5] Added donation_event to member_event_types WIP --- .../members-activity/event-type-filter.js | 57 +++---------------- ghost/admin/app/utils/member-event-types.js | 57 +++++++++++++++++++ .../unit/utils/member-event-types-test.js | 47 +++++++++++++++ 3 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 ghost/admin/app/utils/member-event-types.js create mode 100644 ghost/admin/tests/unit/utils/member-event-types-test.js diff --git a/ghost/admin/app/components/members-activity/event-type-filter.js b/ghost/admin/app/components/members-activity/event-type-filter.js index 50029841c67..1f5cdf05d0b 100644 --- a/ghost/admin/app/components/members-activity/event-type-filter.js +++ b/ghost/admin/app/components/members-activity/event-type-filter.js @@ -1,42 +1,14 @@ import Component from '@glimmer/component'; import {action} from '@ember/object'; +import {getAvailableEventTypes, needDivider, toggleEventType} from 'ghost-admin/utils/member-event-types'; import {inject as service} from '@ember/service'; -const ALL_EVENT_TYPES = [ - {event: 'signup_event', icon: 'filter-dropdown-signups', name: 'Signups', group: 'auth'}, - {event: 'login_event', icon: 'filter-dropdown-logins', name: 'Logins', group: 'auth'}, - {event: 'subscription_event', icon: 'filter-dropdown-paid-subscriptions', name: 'Paid subscriptions', group: 'payments'}, - {event: 'payment_event', icon: 'filter-dropdown-payments', name: 'Payments', group: 'payments'}, - {event: 'newsletter_event', icon: 'filter-dropdown-email-subscriptions', name: 'Email subscriptions', group: 'emails'}, - {event: 'email_opened_event', icon: 'filter-dropdown-email-opened', name: 'Email opened', group: 'emails'}, - {event: 'email_delivered_event', icon: 'filter-dropdown-email-received', name: 'Email received', group: 'emails'}, - {event: 'email_complaint_event', icon: 'filter-dropdown-email-flagged-as-spam', name: 'Email flagged as spam', group: 'emails'}, - {event: 'email_failed_event', icon: 'filter-dropdown-email-bounced', name: 'Email bounced', group: 'emails'}, - {event: 'email_change_event', icon: 'filter-dropdown-email-address-changed', name: 'Email address changed', group: 'emails'} -]; - export default class MembersActivityEventTypeFilter extends Component { @service settings; @service feature; getAvailableEventTypes() { - const extended = [...ALL_EVENT_TYPES]; - - if (this.settings.commentsEnabled !== 'off') { - extended.push({event: 'comment_event', icon: 'filter-dropdown-comments', name: 'Comments', group: 'others'}); - } - if (this.feature.audienceFeedback) { - extended.push({event: 'feedback_event', icon: 'filter-dropdown-feedback', name: 'Feedback', group: 'others'}); - } - if (this.settings.emailTrackClicks) { - extended.push({event: 'click_event', icon: 'filter-dropdown-clicked-in-email', name: 'Clicked link in email', group: 'others'}); - } - - if (this.args.hiddenEvents?.length) { - return extended.filter(t => !this.args.hiddenEvents.includes(t.event)); - } else { - return extended; - } + return getAvailableEventTypes(this.settings, this.feature, this.args.hiddenEvents); } get eventTypes() { @@ -47,30 +19,15 @@ export default class MembersActivityEventTypeFilter extends Component { event: type.event, icon: type.icon, name: type.name, - divider: this.needDivider(type, availableEventTypes[i - 1]), - isSelected: !excludedEvents.includes(type.event) + divider: needDivider(type, availableEventTypes[i - 1]), + isSelected: !excludedEvents.includes(type.event), + isHidden: type.hidden })); } - needDivider(event, prevEvent) { - if (!event?.group || !prevEvent?.group) { - return false; - } - return event.group !== prevEvent.group; - } - @action toggleEventType(eventType) { - const excludedEvents = new Set(this.eventTypes.reject(type => type.isSelected).map(type => type.event)); - - if (excludedEvents.has(eventType)) { - excludedEvents.delete(eventType); - } else { - excludedEvents.add(eventType); - } - - const excludeString = Array.from(excludedEvents).join(','); - - this.args.onChange(excludeString || null); + const newExcludedEvents = toggleEventType(eventType, this.eventTypes, this.feature); + this.args.onChange(newExcludedEvents || null); } } diff --git a/ghost/admin/app/utils/member-event-types.js b/ghost/admin/app/utils/member-event-types.js new file mode 100644 index 00000000000..7f241de727a --- /dev/null +++ b/ghost/admin/app/utils/member-event-types.js @@ -0,0 +1,57 @@ +const ALL_EVENT_TYPES = [ + {event: 'signup_event', icon: 'filter-dropdown-signups', name: 'Signups', group: 'auth'}, + {event: 'login_event', icon: 'filter-dropdown-logins', name: 'Logins', group: 'auth'}, + {event: 'subscription_event', icon: 'filter-dropdown-paid-subscriptions', name: 'Paid subscriptions', group: 'payments'}, + {event: 'payment_event', icon: 'filter-dropdown-payments', name: 'Payments', group: 'payments'}, + {event: 'newsletter_event', icon: 'filter-dropdown-email-subscriptions', name: 'Email subscriptions', group: 'emails'}, + {event: 'email_opened_event', icon: 'filter-dropdown-email-opened', name: 'Email opened', group: 'emails'}, + {event: 'email_delivered_event', icon: 'filter-dropdown-email-received', name: 'Email received', group: 'emails'}, + {event: 'email_complaint_event', icon: 'filter-dropdown-email-flagged-as-spam', name: 'Email flagged as spam', group: 'emails'}, + {event: 'email_failed_event', icon: 'filter-dropdown-email-bounced', name: 'Email bounced', group: 'emails'}, + {event: 'email_change_event', icon: 'filter-dropdown-email-address-changed', name: 'Email address changed', group: 'emails'} +]; + +export function getAvailableEventTypes(settings, feature, hiddenEvents = []) { + const extended = [...ALL_EVENT_TYPES]; + + if (settings.commentsEnabled !== 'off') { + extended.push({event: 'comment_event', icon: 'filter-dropdown-comments', name: 'Comments', group: 'others'}); + } + if (feature.audienceFeedback) { + extended.push({event: 'feedback_event', icon: 'filter-dropdown-feedback', name: 'Feedback', group: 'others'}); + } + if (settings.emailTrackClicks) { + extended.push({event: 'click_event', icon: 'filter-dropdown-clicked-in-email', name: 'Clicked link in email', group: 'others'}); + } + + return extended.filter(t => !hiddenEvents.includes(t.event)); +} + +export function toggleEventType(eventType, eventTypes, feature) { + const excludedEvents = new Set(eventTypes.filter(type => !type.isSelected).map(type => type.event)); + + if (feature.tipsAndDonations && eventType === 'payment_event') { + if (excludedEvents.has('payment_event')) { + excludedEvents.delete('payment_event'); + excludedEvents.delete('donation_event'); + } else { + excludedEvents.add('payment_event'); + excludedEvents.add('donation_event'); + } + } else { + if (excludedEvents.has(eventType)) { + excludedEvents.delete(eventType); + } else { + excludedEvents.add(eventType); + } + } + + return Array.from(excludedEvents).join(','); +} + +export function needDivider(event, prevEvent) { + if (!event?.group || !prevEvent?.group) { + return false; + } + return event.group !== prevEvent.group; +} diff --git a/ghost/admin/tests/unit/utils/member-event-types-test.js b/ghost/admin/tests/unit/utils/member-event-types-test.js new file mode 100644 index 00000000000..87b653f6e56 --- /dev/null +++ b/ghost/admin/tests/unit/utils/member-event-types-test.js @@ -0,0 +1,47 @@ +import {describe, it} from 'mocha'; +import {expect} from 'chai'; +import {getAvailableEventTypes, needDivider, toggleEventType} from 'ghost-admin/utils/member-event-types'; + +describe('Unit | Utility | event-type-utils', function () { + it('should return available event types with settings and features applied', function () { + const settings = { + commentsEnabled: 'on', + emailTrackClicks: true + }; + const feature = { + audienceFeedback: true, + tipsAndDonations: true + }; + const hiddenEvents = []; + + const eventTypes = getAvailableEventTypes(settings, feature, hiddenEvents); + + expect(eventTypes).to.deep.include({event: 'comment_event', icon: 'filter-dropdown-comments', name: 'Comments', group: 'others'}); + expect(eventTypes).to.deep.include({event: 'feedback_event', icon: 'filter-dropdown-feedback', name: 'Feedback', group: 'others'}); + expect(eventTypes).to.deep.include({event: 'click_event', icon: 'filter-dropdown-clicked-in-email', name: 'Clicked link in email', group: 'others'}); + expect(eventTypes).to.deep.include({event: 'donation_event', icon: 'filter-dropdown-donations', name: 'Donations', group: 'payments', hidden: true}); + }); + + it('should toggle both payment_event and donation_event when tipsAndDonations is enabled', function () { + const eventTypes = [ + {event: 'payment_event', isSelected: true}, + {event: 'donation_event', isSelected: true} + ]; + const feature = { + tipsAndDonations: true + }; + + const newExcludedEvents = toggleEventType('payment_event', eventTypes, feature); + + expect(newExcludedEvents).to.equal('payment_event,donation_event'); + }); + + it('should return correct divider need based on event groups', function () { + const event = {group: 'auth'}; + const prevEvent = {group: 'payments'}; + + const result = needDivider(event, prevEvent); + + expect(result).to.be.true; + }); +}); From 4c3e527ccedc9802e6509de6718d347ff6d781dd Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Wed, 21 Aug 2024 17:52:14 +0700 Subject: [PATCH 2/5] Added unit tests --- ghost/admin/app/utils/member-event-types.js | 2 +- .../unit/utils/member-event-types-test.js | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/ghost/admin/app/utils/member-event-types.js b/ghost/admin/app/utils/member-event-types.js index 7f241de727a..3ccc8ef626b 100644 --- a/ghost/admin/app/utils/member-event-types.js +++ b/ghost/admin/app/utils/member-event-types.js @@ -1,4 +1,4 @@ -const ALL_EVENT_TYPES = [ +export const ALL_EVENT_TYPES = [ {event: 'signup_event', icon: 'filter-dropdown-signups', name: 'Signups', group: 'auth'}, {event: 'login_event', icon: 'filter-dropdown-logins', name: 'Logins', group: 'auth'}, {event: 'subscription_event', icon: 'filter-dropdown-paid-subscriptions', name: 'Paid subscriptions', group: 'payments'}, diff --git a/ghost/admin/tests/unit/utils/member-event-types-test.js b/ghost/admin/tests/unit/utils/member-event-types-test.js index 87b653f6e56..41f7857cf43 100644 --- a/ghost/admin/tests/unit/utils/member-event-types-test.js +++ b/ghost/admin/tests/unit/utils/member-event-types-test.js @@ -1,6 +1,6 @@ +import {ALL_EVENT_TYPES, getAvailableEventTypes, needDivider, toggleEventType} from 'ghost-admin/utils/member-event-types'; import {describe, it} from 'mocha'; import {expect} from 'chai'; -import {getAvailableEventTypes, needDivider, toggleEventType} from 'ghost-admin/utils/member-event-types'; describe('Unit | Utility | event-type-utils', function () { it('should return available event types with settings and features applied', function () { @@ -19,7 +19,6 @@ describe('Unit | Utility | event-type-utils', function () { expect(eventTypes).to.deep.include({event: 'comment_event', icon: 'filter-dropdown-comments', name: 'Comments', group: 'others'}); expect(eventTypes).to.deep.include({event: 'feedback_event', icon: 'filter-dropdown-feedback', name: 'Feedback', group: 'others'}); expect(eventTypes).to.deep.include({event: 'click_event', icon: 'filter-dropdown-clicked-in-email', name: 'Clicked link in email', group: 'others'}); - expect(eventTypes).to.deep.include({event: 'donation_event', icon: 'filter-dropdown-donations', name: 'Donations', group: 'payments', hidden: true}); }); it('should toggle both payment_event and donation_event when tipsAndDonations is enabled', function () { @@ -44,4 +43,34 @@ describe('Unit | Utility | event-type-utils', function () { expect(result).to.be.true; }); + + it('should return only base event types when no settings or features are enabled', function () { + const settings = { + commentsEnabled: 'off', + emailTrackClicks: false + }; + const feature = { + audienceFeedback: false, + tipsAndDonations: false + }; + const hiddenEvents = []; + + const eventTypes = getAvailableEventTypes(settings, feature, hiddenEvents); + + expect(eventTypes).to.deep.equal(ALL_EVENT_TYPES); + }); + + it('should toggle only the selected event when tipsAndDonations is disabled', function () { + const eventTypes = [ + {event: 'payment_event', isSelected: true}, + {event: 'donation_event', isSelected: true} + ]; + const feature = { + tipsAndDonations: false + }; + + const newExcludedEvents = toggleEventType('payment_event', eventTypes, feature); + + expect(newExcludedEvents).to.equal('payment_event'); + }); }); From 256d0aa47fddb53ed0d8edc8043e74e73458e09d Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Thu, 22 Aug 2024 14:13:06 +0700 Subject: [PATCH 3/5] wip acceptance test --- .../mirage/factories/member-activity-event.js | 1 + .../tests/acceptance/members-activity-test.js | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/ghost/admin/mirage/factories/member-activity-event.js b/ghost/admin/mirage/factories/member-activity-event.js index 26436df768a..2f292128388 100644 --- a/ghost/admin/mirage/factories/member-activity-event.js +++ b/ghost/admin/mirage/factories/member-activity-event.js @@ -7,6 +7,7 @@ const EVENT_TYPES = [ 'login_event', 'subscription_event', 'payment_event', + 'donation_event', 'login_event', 'signup_event', 'email_delivered_event', diff --git a/ghost/admin/tests/acceptance/members-activity-test.js b/ghost/admin/tests/acceptance/members-activity-test.js index a62b6f9ffa0..92dd5fd621c 100644 --- a/ghost/admin/tests/acceptance/members-activity-test.js +++ b/ghost/admin/tests/acceptance/members-activity-test.js @@ -1,3 +1,4 @@ +import moment from 'moment-timezone'; import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support'; import {currentURL} from '@ember/test-helpers'; import {describe, it} from 'mocha'; @@ -40,4 +41,33 @@ describe('Acceptance: Members activity', function () { expect(currentURL()).to.equal('/members-activity'); }); }); + + describe('as owner', function () { + beforeEach(async function () { + const role = this.server.create('role', {name: 'Owner'}); + this.server.create('user', {roles: [role]}); + + await authenticateSession(); + }); + + it('renders', async function () { + await visit('/members-activity'); + expect(currentURL()).to.equal('/members-activity'); + }); + }); + + // describe('members activity filter', function () { + // beforeEach(async function () { + // const role = this.server.create('role', {name: 'Administrator'}); + // await this.server.create('user', {roles: [role]}); + + // await authenticateSession(); + // await this.server.createList('member-activity-event', 10, {createdAt: moment('2024-08-18 08:18:08').format('YYYY-MM-DD HH:mm:ss')}); + // }); + + // it('renders', async function () { + // await visit('/members-activity'); + // expect(currentURL()).to.equal('/members-activity'); + // }); + // }); }); From db724ab1fafce0c1c03aed7ccf6e846bd63886f5 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Thu, 22 Aug 2024 14:42:07 +0700 Subject: [PATCH 4/5] Fixed linting --- ghost/admin/tests/acceptance/members-activity-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/admin/tests/acceptance/members-activity-test.js b/ghost/admin/tests/acceptance/members-activity-test.js index 92dd5fd621c..03e7969a73d 100644 --- a/ghost/admin/tests/acceptance/members-activity-test.js +++ b/ghost/admin/tests/acceptance/members-activity-test.js @@ -1,4 +1,4 @@ -import moment from 'moment-timezone'; +// import moment from 'moment-timezone'; import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support'; import {currentURL} from '@ember/test-helpers'; import {describe, it} from 'mocha'; From 32c6e0a13f0ac4ab250610bfeaa204f738236089 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Thu, 22 Aug 2024 17:16:35 +0700 Subject: [PATCH 5/5] Added acceptance test --- .../members-activity/event-type-filter.hbs | 5 +- .../members-activity/event-type-filter.js | 5 +- ghost/admin/app/utils/member-event-types.js | 4 +- .../tests/acceptance/members-activity-test.js | 56 ++++++++++++++----- .../unit/utils/member-event-types-test.js | 34 +++++------ 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/ghost/admin/app/components/members-activity/event-type-filter.hbs b/ghost/admin/app/components/members-activity/event-type-filter.hbs index 112e84bfda2..9a3a1419c83 100644 --- a/ghost/admin/app/components/members-activity/event-type-filter.hbs +++ b/ghost/admin/app/components/members-activity/event-type-filter.hbs @@ -1,5 +1,5 @@ - + {{svg-jar "filter"}} Filter events @@ -13,7 +13,7 @@ {{#if type.divider}}
  • {{/if}} -
  • +