c5bb2e5dc7
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
117 lines
3.2 KiB
JavaScript
117 lines
3.2 KiB
JavaScript
import moment from 'moment-timezone';
|
||
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
|
||
|
||
export function getSubscriptionData(sub) {
|
||
const data = {
|
||
...sub,
|
||
attribution: {
|
||
...sub.attribution,
|
||
referrerSource: sub.attribution?.referrer_source || 'Unknown',
|
||
referrerMedium: sub.attribution?.referrer_medium || '-'
|
||
},
|
||
startDate: sub.start_date ? moment(sub.start_date).format('D MMM YYYY') : '-',
|
||
validUntil: validUntil(sub),
|
||
hasEnded: isCanceled(sub),
|
||
willEndSoon: isSetToCancel(sub),
|
||
cancellationReason: sub.cancellation_reason,
|
||
price: {
|
||
...sub.price,
|
||
currencySymbol: getSymbol(sub.price.currency),
|
||
nonDecimalAmount: getNonDecimal(sub.price.amount)
|
||
},
|
||
isComplimentary: isComplimentary(sub),
|
||
compExpiry: compExpiry(sub),
|
||
trialUntil: trialUntil(sub)
|
||
};
|
||
|
||
data.priceLabel = priceLabel(data);
|
||
data.validityDetails = validityDetails(data, !!data.priceLabel);
|
||
|
||
return data;
|
||
}
|
||
|
||
export function validUntil(sub) {
|
||
// If a subscription has been canceled immediately, don't render the end of validity date
|
||
// Reason: we don't store the exact cancelation date in the subscription object
|
||
if (sub.status === 'canceled' && !sub.cancel_at_period_end) {
|
||
return '';
|
||
}
|
||
|
||
// Otherwise, show the current period end date
|
||
if (sub.current_period_end) {
|
||
return moment(sub.current_period_end).format('D MMM YYYY');
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
export function isActive(sub) {
|
||
return ['active', 'trialing', 'past_due', 'unpaid'].includes(sub.status);
|
||
}
|
||
|
||
export function isComplimentary(sub) {
|
||
return !sub.id;
|
||
}
|
||
|
||
export function isCanceled(sub) {
|
||
return sub.status === 'canceled';
|
||
}
|
||
|
||
export function isSetToCancel(sub) {
|
||
return sub.cancel_at_period_end && isActive(sub);
|
||
}
|
||
|
||
export function compExpiry(sub) {
|
||
if (!sub.id && sub.tier && sub.tier.expiry_at) {
|
||
return moment(sub.tier.expiry_at).utc().format('D MMM YYYY');
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
export function trialUntil(sub) {
|
||
const inTrialMode = sub.trial_end_at && moment(sub.trial_end_at).isAfter(new Date(), 'day');
|
||
if (inTrialMode) {
|
||
return moment(sub.trial_end_at).format('D MMM YYYY');
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
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 `${separator}Ended${space}${data.validUntil}`;
|
||
}
|
||
|
||
if (data.willEndSoon) {
|
||
return `${separator}Has access until${space}${data.validUntil}`;
|
||
}
|
||
|
||
if (data.trialUntil) {
|
||
return `${separator}Ends ${data.trialUntil}`;
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|