From 17710c3e8e22d819936d87d952d7ac5e652f9213 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Fri, 4 Mar 2022 16:32:32 +0000 Subject: [PATCH] Switched members table to show info from most-recently-updated subscription instead of first in array no issue - members having multiple subscriptions is not really expected but if it does happen then the most recently updated subscription is most likely to be the one that we're interested in showing the details of in the members table - added `{{most-recently-updated arr}}` helper+function that will return the item in the array argument with the most recent "updated at" value - uses `updatedAtUTC` or `updatedAt` or `updated_at` values so it will work against any of our models or un-transformed API response objects - updated `` to use a `mostRecentSubscription` getter when showing subscription data rather than assuming the first object in the subscriptions array should be displayed --- .../gh-members-list-item-column.hbs | 20 +++--- .../components/gh-members-list-item-column.js | 17 ++--- .../app/helpers/most-recently-updated.js | 19 ++++++ .../helpers/most-recently-updated-test.js | 64 +++++++++++++++++++ 4 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 ghost/admin/app/helpers/most-recently-updated.js create mode 100644 ghost/admin/tests/unit/helpers/most-recently-updated-test.js diff --git a/ghost/admin/app/components/gh-members-list-item-column.hbs b/ghost/admin/app/components/gh-members-list-item-column.hbs index 6d448be947..844f5c27ac 100644 --- a/ghost/admin/app/components/gh-members-list-item-column.hbs +++ b/ghost/admin/app/components/gh-members-list-item-column.hbs @@ -56,8 +56,8 @@ {{else if (eq @filterColumn 'subscriptions.status')}} - {{#if (not (is-empty this.subscriptionStatus))}} - {{capitalize this.subscriptionStatus}} + {{#if (not (is-empty this.mostRecentSubscription.status))}} + {{capitalize this.mostRecentSubscription.status}} {{else}} - {{/if}} @@ -65,8 +65,8 @@ {{else if (eq @filterColumn 'subscriptions.plan_interval')}} - {{#if (not (is-empty this.billingPeriod))}} - {{capitalize this.billingPeriod}} + {{#if (not (is-empty this.mostRecentSubscription.price.interval))}} + {{capitalize this.mostRecentSubscription.price.interval}} {{else}} - {{/if}} @@ -74,9 +74,9 @@ {{else if (eq @filterColumn 'subscriptions.start_date')}} - {{#if (not (is-empty @member.subscriptions.firstObject.start_date))}} - {{moment-format (moment-site-tz @member.subscriptions.firstObject.start_date) "D MMM YYYY"}} -
{{moment-from-now @member.subscriptions.firstObject.start_date}}
+ {{#if (not (is-empty this.mostRecentSubscription.start_date))}} + {{moment-format (moment-site-tz this.mostRecentSubscription.start_date) "D MMM YYYY"}} +
{{moment-from-now this.mostRecentSubscription.start_date}}
{{else}} - {{/if}} @@ -84,9 +84,9 @@ {{else if (eq @filterColumn 'subscriptions.current_period_end')}} - {{#if (not (is-empty @member.subscriptions.firstObject.current_period_end))}} - {{moment-format (moment-site-tz @member.subscriptions.firstObject.current_period_end) "D MMM YYYY"}} -
{{moment-from-now @member.subscriptions.firstObject.current_period_end}}
+ {{#if (not (is-empty this.mostRecentSubscription.current_period_end))}} + {{moment-format (moment-site-tz this.mostRecentSubscription.current_period_end) "D MMM YYYY"}} +
{{moment-from-now this.mostRecentSubscription.current_period_end}}
{{else}} - {{/if}} diff --git a/ghost/admin/app/components/gh-members-list-item-column.js b/ghost/admin/app/components/gh-members-list-item-column.js index 7a33b3070f..d43a20c3ac 100644 --- a/ghost/admin/app/components/gh-members-list-item-column.js +++ b/ghost/admin/app/components/gh-members-list-item-column.js @@ -1,4 +1,6 @@ import Component from '@glimmer/component'; +import {get} from '@ember/object'; +import {mostRecentlyUpdated} from 'ghost-admin/helpers/most-recently-updated'; export default class GhMembersListItemColumn extends Component { constructor(...args) { @@ -6,23 +8,16 @@ export default class GhMembersListItemColumn extends Component { } get labels() { - const labelData = this.args.member.get('labels') || []; + const labelData = get(this.args.member, 'labels') || []; return labelData.map(label => label.name).join(', '); } get products() { - const productData = this.args.member.get('products') || []; + const productData = get(this.args.member, 'products') || []; return productData.map(product => product.name).join(', '); } - get subscriptionStatus() { - const subscriptions = this.args.member.get('subscriptions') || []; - return subscriptions[0]?.status; - } - - get billingPeriod() { - const subscriptions = this.args.member.get('subscriptions') || []; - const billingPeriod = subscriptions[0]?.price?.interval; - return billingPeriod; + get mostRecentSubscription() { + return mostRecentlyUpdated(get(this.args.member, 'subscriptions')); } } diff --git a/ghost/admin/app/helpers/most-recently-updated.js b/ghost/admin/app/helpers/most-recently-updated.js new file mode 100644 index 0000000000..0c48b1dc2f --- /dev/null +++ b/ghost/admin/app/helpers/most-recently-updated.js @@ -0,0 +1,19 @@ +import moment from 'moment'; +import {helper} from '@ember/component/helper'; + +export function mostRecentlyUpdated(objs) { + const items = [...(objs || [])]; + + (items || []).sort((a, b) => { + const momentA = moment(a.updatedAtUTC || a.updatedAt || a.updated_at); + const momentB = moment(b.updatedAtUTC || b.updatedAt || b.updated_at); + + return momentB.valueOf() - momentA.valueOf(); + }); + + return items[0] || null; +} + +export default helper(function ([items = []]) { + return mostRecentlyUpdated(items); +}); diff --git a/ghost/admin/tests/unit/helpers/most-recently-updated-test.js b/ghost/admin/tests/unit/helpers/most-recently-updated-test.js new file mode 100644 index 0000000000..1a76473312 --- /dev/null +++ b/ghost/admin/tests/unit/helpers/most-recently-updated-test.js @@ -0,0 +1,64 @@ +import moment from 'moment'; +import {describe, it} from 'mocha'; +import {expect} from 'chai'; +import {mostRecentlyUpdated} from 'ghost-admin/helpers/most-recently-updated'; + +describe('Unit: Helper: most-recently-updated', function () { + it('returns most recent - updatedAtUTC', function () { + const a = {updatedAtUTC: moment.utc('2022-03-04 16:10')}; + const b = {updatedAtUTC: moment.utc('2022-03-03 16:10')}; + const c = {updatedAtUTC: moment.utc('2022-03-04 16:20')}; + + const subs = [a, b, c]; + + expect(mostRecentlyUpdated(subs)).to.equal(c); + }); + + it('returns most recent - updatedAt', function () { + const a = {updatedAt: moment('2022-03-04 16:10')}; + const b = {updatedAt: moment('2022-03-05 16:10')}; + const c = {updatedAt: moment('2022-03-04 16:20')}; + + const subs = [a, b, c]; + + expect(mostRecentlyUpdated(subs)).to.equal(b); + }); + + it('returns most recent - updated_at', function () { + const a = {updated_at: '2022-03-04 16:10'}; + const b = {updated_at: '2022-03-03 16:10'}; + const c = {updated_at: '2022-03-04 16:20'}; + + const subs = [a, b, c]; + + expect(mostRecentlyUpdated(subs)).to.equal(c); + }); + + it('handles a single-element array', function () { + const a = {updated_at: '2022-02-22'}; + + expect(mostRecentlyUpdated([a])).to.equal(a); + }); + + it('handles null', function () { + expect(mostRecentlyUpdated(null)).to.equal(null); + }); + + it('handles empty array', function () { + expect(mostRecentlyUpdated([])).to.equal(null); + }); + + it('does not modify original array', function () { + const a = {updated_at: '2022-03-04 16:10'}; + const b = {updated_at: '2022-03-03 16:10'}; + const c = {updated_at: '2022-03-04 16:20'}; + + const subs = [a, b, c]; + + mostRecentlyUpdated(subs); + + expect(subs[0]).to.equal(a); + expect(subs[1]).to.equal(b); + expect(subs[2]).to.equal(c); + }); +});