🐛 Fixed member subscription details in Admin (#20619)

fixes https://linear.app/tryghost/issue/ONC-189

- commit 4084a3d introduced a regression that caused member subscription
details to not be rendered for active/canceled subscriptions
- with this fix, the rendering logic in Admin for member subscription
details has been fully moved to a helper and is now covered by
additional unit tests
This commit is contained in:
Sag 2024-07-18 12:14:38 +02:00 committed by GitHub
parent cd17b94e9c
commit c5bb2e5dc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 187 additions and 123 deletions

View File

@ -138,26 +138,8 @@
{{/if}} {{/if}}
</h3> </h3>
<div> <div>
{{#if sub.trialUntil}} <span class="gh-cp-membertier-pricelabel">{{sub.priceLabel}}</span>
<span class="gh-cp-membertier-pricelabel">Free trial </span>
{{else}}
{{#if (or (eq sub.price.nickname "Monthly") (eq sub.price.nickname "Yearly"))}}
{{else}}
<span class="gh-cp-membertier-pricelabel">{{sub.price.nickname}}</span>
{{/if}}
{{/if}}
{{#if sub.trialUntil}}
<span class="gh-cp-membertier-renewal"> &ndash; </span>
<span class="gh-cp-membertier-renewal">{{sub.validityDetails}}</span> <span class="gh-cp-membertier-renewal">{{sub.validityDetails}}</span>
{{/if}}
{{#if sub.compExpiry}}
<span class="gh-cp-membertier-renewal"> &ndash; </span>
<span class="gh-cp-membertier-renewal">{{sub.validityDetails}}</span>
{{/if}}
</div> </div>
<Member::SubscriptionDetailBox @sub={{sub}} @index={{index}} /> <Member::SubscriptionDetailBox @sub={{sub}} @index={{index}} />
</div> </div>

View File

@ -24,7 +24,8 @@ export function getSubscriptionData(sub) {
trialUntil: trialUntil(sub) trialUntil: trialUntil(sub)
}; };
data.validityDetails = validityDetails(data); data.priceLabel = priceLabel(data);
data.validityDetails = validityDetails(data, !!data.priceLabel);
return data; return data;
} }
@ -77,22 +78,39 @@ export function trialUntil(sub) {
return undefined; return undefined;
} }
export function validityDetails(data) { export function validityDetails(data, separatorNeeded = false) {
if (data.isComplimentary && data.compExpiry) { const separator = separatorNeeded ? ' ' : '';
return `Expires ${data.compExpiry}`; const space = data.validUntil ? ' ' : '';
if (data.isComplimentary) {
if (data.compExpiry) {
return `${separator}Expires ${data.compExpiry}`;
} else {
return '';
}
} }
if (data.hasEnded) { if (data.hasEnded) {
return `Ended ${data.validUntil}`; return `${separator}Ended${space}${data.validUntil}`;
} }
if (data.willEndSoon) { if (data.willEndSoon) {
return `Has access until ${data.validUntil}`; return `${separator}Has access until${space}${data.validUntil}`;
} }
if (data.trialUntil) { if (data.trialUntil) {
return `Ends ${data.trialUntil}`; return `${separator}Ends ${data.trialUntil}`;
} }
return `Renews ${data.validUntil}`; return `${separator}Renews${space}${data.validUntil}`;
}
export function priceLabel(data) {
if (data.trialUntil) {
return 'Free trial';
}
if (data.price.nickname && data.price.nickname.length > 0 && data.price.nickname !== 'Monthly' && data.price.nickname !== 'Yearly') {
return data.price.nickname;
}
} }

View File

@ -1,5 +1,5 @@
import moment from 'moment-timezone'; import moment from 'moment-timezone';
import {compExpiry, getSubscriptionData, isActive, isCanceled, isComplimentary, isSetToCancel, trialUntil, validUntil, validityDetails} from 'ghost-admin/utils/subscription-data'; import {compExpiry, getSubscriptionData, isActive, isCanceled, isComplimentary, isSetToCancel, priceLabel, trialUntil, validUntil, validityDetails} from 'ghost-admin/utils/subscription-data';
import {describe, it} from 'mocha'; import {describe, it} from 'mocha';
import {expect} from 'chai'; import {expect} from 'chai';
@ -165,6 +165,26 @@ describe('Unit: Util: subscription-data', function () {
}); });
}); });
describe('priceLabel', function () {
it('returns "Free trial" for trial subscriptions', function () {
let data = {trialUntil: '31 May 2021'};
expect(priceLabel(data)).to.equal('Free trial');
});
it('returns nothing if the price nickname is the default "monthly" or "yearly"', function () {
let data = {price: {nickname: 'Monthly'}};
expect(priceLabel(data)).to.be.undefined;
data = {price: {nickname: 'Yearly'}};
expect(priceLabel(data)).to.be.undefined;
});
it('returns the price nickname for non-default prices', function () {
let data = {price: {nickname: 'Custom'}};
expect(priceLabel(data)).to.equal('Custom');
});
});
describe('validityDetails', function () { describe('validityDetails', function () {
it('returns "Expires {compExpiry}" for expired complimentary subscriptions', function () { it('returns "Expires {compExpiry}" for expired complimentary subscriptions', function () {
let data = { let data = {
@ -174,6 +194,14 @@ describe('Unit: Util: subscription-data', function () {
expect(validityDetails(data)).to.equal('Expires 31 May 2021'); expect(validityDetails(data)).to.equal('Expires 31 May 2021');
}); });
it('returns "" for forever complimentary subscriptions', function () {
let data = {
isComplimentary: true,
compExpiry: undefined
};
expect(validityDetails(data)).to.equal('');
});
it('returns "Ended {validUntil}" for canceled subscriptions', function () { it('returns "Ended {validUntil}" for canceled subscriptions', function () {
let data = { let data = {
hasEnded: true, hasEnded: true,
@ -228,6 +256,7 @@ describe('Unit: Util: subscription-data', function () {
validUntil: '31 May 2021', validUntil: '31 May 2021',
willEndSoon: false, willEndSoon: false,
trialUntil: undefined, trialUntil: undefined,
priceLabel: undefined,
validityDetails: 'Renews 31 May 2021' validityDetails: 'Renews 31 May 2021'
}); });
}); });
@ -254,7 +283,8 @@ describe('Unit: Util: subscription-data', function () {
validUntil: '31 May 2222', validUntil: '31 May 2222',
willEndSoon: false, willEndSoon: false,
trialUntil: '31 May 2222', trialUntil: '31 May 2222',
validityDetails: 'Ends 31 May 2222' priceLabel: 'Free trial',
validityDetails: ' Ends 31 May 2222'
}); });
}); });
@ -280,7 +310,8 @@ describe('Unit: Util: subscription-data', function () {
validUntil: '', validUntil: '',
willEndSoon: false, willEndSoon: false,
trialUntil: undefined, trialUntil: undefined,
validityDetails: 'Ended ' priceLabel: undefined,
validityDetails: 'Ended'
}); });
}); });
@ -306,11 +337,42 @@ describe('Unit: Util: subscription-data', function () {
validUntil: '31 May 2021', validUntil: '31 May 2021',
willEndSoon: true, willEndSoon: true,
trialUntil: undefined, trialUntil: undefined,
priceLabel: undefined,
validityDetails: 'Has access until 31 May 2021' validityDetails: 'Has access until 31 May 2021'
}); });
}); });
it('returns the correct data for a complimentary subscription', function () { it('returns the correct data for a complimentary subscription active forever', function () {
let sub = {
id: null,
status: 'active',
cancel_at_period_end: false,
current_period_end: '2021-05-31',
trial_end_at: null,
tier: {
expiry_at: null
},
price: {
currency: 'usd',
amount: 0,
nickname: 'Complimentary'
}
};
let data = getSubscriptionData(sub);
expect(data).to.include({
isComplimentary: true,
compExpiry: undefined,
hasEnded: false,
validUntil: '31 May 2021',
willEndSoon: false,
trialUntil: undefined,
priceLabel: 'Complimentary',
validityDetails: ''
});
});
it('returns the correct data for a complimentary subscription with an expiration date', function () {
let sub = { let sub = {
id: null, id: null,
status: 'active', status: 'active',
@ -322,7 +384,8 @@ describe('Unit: Util: subscription-data', function () {
}, },
price: { price: {
currency: 'usd', currency: 'usd',
amount: 0 amount: 0,
nickname: 'Complimentary'
} }
}; };
let data = getSubscriptionData(sub); let data = getSubscriptionData(sub);
@ -334,7 +397,8 @@ describe('Unit: Util: subscription-data', function () {
validUntil: '31 May 2021', validUntil: '31 May 2021',
willEndSoon: false, willEndSoon: false,
trialUntil: undefined, trialUntil: undefined,
validityDetails: 'Expires 31 May 2021' priceLabel: 'Complimentary',
validityDetails: ' Expires 31 May 2021'
}); });
}); });
}); });