diff --git a/ghost/admin/app/components/gh-member-settings-form.hbs b/ghost/admin/app/components/gh-member-settings-form.hbs index 7813d11375..c6a3234c0a 100644 --- a/ghost/admin/app/components/gh-member-settings-form.hbs +++ b/ghost/admin/app/components/gh-member-settings-form.hbs @@ -111,122 +111,104 @@
{{#each tier.subscriptions as |sub index|}} -
-
-
- {{sub.price.currencySymbol}} - {{format-number sub.price.nonDecimalAmount}} +
+
+
+ {{sub.price.currencySymbol}} + {{format-number sub.price.nonDecimalAmount}} +
+
{{if (eq sub.price.interval "year") "yearly" "monthly"}}
-
{{if (eq sub.price.interval "year") "yearly" "monthly"}}
-
-
-

- {{tier.name}} - {{#if (eq sub.status "canceled")}} - Canceled - {{else if sub.cancel_at_period_end}} - Canceled - {{else if sub.compExpiry}} - Active - {{else if sub.trialUntil}} - Active - {{else}} - Active - {{/if}} - {{#if (gt tier.subscriptions.length 1)}} - {{tier.subscriptions.length}} subscriptions - {{/if}} -

-
- {{#if sub.trialUntil}} - Free trial - {{else}} - {{#if (or (eq sub.price.nickname "Monthly") (eq sub.price.nickname "Yearly"))}} +
+

+ {{tier.name}} + {{#if (eq sub.status "canceled")}} + Canceled + {{else if sub.cancel_at_period_end}} + Canceled + {{else if sub.compExpiry}} + Active + {{else if sub.trialUntil}} + Active {{else}} - {{sub.price.nickname}} + Active {{/if}} - {{/if}} - - {{#if sub.trialUntil}} - + {{#if (gt tier.subscriptions.length 1)}} + {{tier.subscriptions.length}} subscriptions + {{/if}} +

+
+ {{sub.priceLabel}} {{sub.validityDetails}} - {{/if}} - - {{#if sub.compExpiry}} - - {{sub.validityDetails}} - {{/if}} - - +
+
- -
- {{#if sub.isComplimentary}} - - - - {{svg-jar "dotdotdot"}} - - - - -
  • - -
  • -
    -
    - {{else}} - - - - {{svg-jar "dotdotdot"}} - - - - -
  • - - View Stripe customer - -
  • -
  • -
  • - - View Stripe subscription - -
  • -
  • - {{#if (not-eq sub.status "canceled")}} - {{#if sub.cancel_at_period_end}} - - {{else}} - +
  • +
    +
    + {{else}} + + + + {{svg-jar "dotdotdot"}} + + + + +
  • + + View Stripe customer + +
  • +
  • +
  • + + View Stripe subscription + +
  • +
  • + {{#if (not-eq sub.status "canceled")}} + {{#if sub.cancel_at_period_end}} + + {{else}} + + {{/if}} {{/if}} - {{/if}} -
  • -
    -
    - {{/if}} -
    + + + + {{/if}} +
    {{/each}} {{#if (eq tier.subscriptions.length 0)}} diff --git a/ghost/admin/app/utils/subscription-data.js b/ghost/admin/app/utils/subscription-data.js index 347265ace4..9a396963d9 100644 --- a/ghost/admin/app/utils/subscription-data.js +++ b/ghost/admin/app/utils/subscription-data.js @@ -24,7 +24,8 @@ export function getSubscriptionData(sub) { trialUntil: trialUntil(sub) }; - data.validityDetails = validityDetails(data); + data.priceLabel = priceLabel(data); + data.validityDetails = validityDetails(data, !!data.priceLabel); return data; } @@ -77,22 +78,39 @@ export function trialUntil(sub) { return undefined; } -export function validityDetails(data) { - if (data.isComplimentary && data.compExpiry) { - return `Expires ${data.compExpiry}`; +export function validityDetails(data, separatorNeeded = false) { + const separator = separatorNeeded ? ' – ' : ''; + const space = data.validUntil ? ' ' : ''; + + if (data.isComplimentary) { + if (data.compExpiry) { + return `${separator}Expires ${data.compExpiry}`; + } else { + return ''; + } } if (data.hasEnded) { - return `Ended ${data.validUntil}`; + return `${separator}Ended${space}${data.validUntil}`; } if (data.willEndSoon) { - return `Has access until ${data.validUntil}`; + return `${separator}Has access until${space}${data.validUntil}`; } 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; + } } diff --git a/ghost/admin/tests/unit/utils/subscription-data-test.js b/ghost/admin/tests/unit/utils/subscription-data-test.js index 5b396b20d8..660b4d72c4 100644 --- a/ghost/admin/tests/unit/utils/subscription-data-test.js +++ b/ghost/admin/tests/unit/utils/subscription-data-test.js @@ -1,5 +1,5 @@ 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 {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 () { it('returns "Expires {compExpiry}" for expired complimentary subscriptions', function () { let data = { @@ -174,6 +194,14 @@ describe('Unit: Util: subscription-data', function () { 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 () { let data = { hasEnded: true, @@ -228,6 +256,7 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: false, trialUntil: undefined, + priceLabel: undefined, validityDetails: 'Renews 31 May 2021' }); }); @@ -254,7 +283,8 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2222', willEndSoon: false, 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: '', willEndSoon: false, trialUntil: undefined, - validityDetails: 'Ended ' + priceLabel: undefined, + validityDetails: 'Ended' }); }); @@ -306,11 +337,42 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: true, trialUntil: undefined, + priceLabel: undefined, 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 = { id: null, status: 'active', @@ -322,7 +384,8 @@ describe('Unit: Util: subscription-data', function () { }, price: { currency: 'usd', - amount: 0 + amount: 0, + nickname: 'Complimentary' } }; let data = getSubscriptionData(sub); @@ -334,7 +397,8 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: false, trialUntil: undefined, - validityDetails: 'Expires 31 May 2021' + priceLabel: 'Complimentary', + validityDetails: ' – Expires 31 May 2021' }); }); });