diff --git a/ghost/admin/app/components/gh-member-settings-form-cp.hbs b/ghost/admin/app/components/gh-member-settings-form-cp.hbs new file mode 100644 index 0000000000..dce349be4f --- /dev/null +++ b/ghost/admin/app/components/gh-member-settings-form-cp.hbs @@ -0,0 +1,224 @@ +
+ +
+
+
+
+ {{#if (or this.member.name this.member.email)}} + + {{else}} +
+ N +
+ {{/if}} +
+

+ {{or this.member.name this.member.email}} +

+

+ {{#if (and this.member.name this.member.email)}} + {{this.member.email}} + {{/if}} +

+ {{#unless this.member.isNew}} +

+ {{#if this.member.geolocation}} + {{#if (and (eq this.member.geolocation.country_code "US") @member.geolocation.region)}} + {{this.member.geolocation.region}}, US + {{else}} + {{or this.member.geolocation.country "Unknown location"}} + {{/if}} + {{else}} + Unknown location + {{/if}} + – Created on {{moment-format @member.createdAtUTC "D MMM YYYY"}} +

+ {{/unless}} +
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + +
+ + + + + + + + + + +

Maximum: 500 characters. You’ve used + {{gh-count-down-characters this.scratchMember.note 500}}

+
+ + +
+
+

Subscribed to newsletter

+

If disabled, member will not receive newsletter emails

+
+
+ +
+
+
+
+
+ + {{#if this.canShowStripeInfo}} + {{#if this.subscriptions}} +

Stripe info

+
+
+
+
+ + + + + + + + + +
Name + {{#if customer.name}} + {{customer.name}} + {{else}} + No name + {{/if}} +
Email + {{#if customer.email}} + {{customer.email}} + {{else}} + No email + {{/if}} +
+
+
+
+
+ + + + + +
Card + No card info +
+
+
+
+
+ + View on Stripe + +
+
+ {{/if}} + {{/if}} + +

Products

+
+
    +
  1. +
    +
    +
  2. +
  3. +
    +

    + Product 1 +

    +

    + Description +

    +
    + +
    +
    + Edit + +
    +
    +
  4. +
  5. +
    +

    + Product 2 Cancelled +

    +

    + Cancelled product +

    +
    + +
    +
    + +
    +
    +
  6. +
+ + {{!-- --}} +
+
+
+
+
Emails received
+
{{@member.emailCount}}
+
Emails opened
+
{{@member.emailOpenedCount}}
+
Avg. open rate
+
+ {{#if (is-empty @member.emailOpenRate)}} + + {{else}} + {{@member.emailOpenRate}}% + {{/if}} +
+
+ +

Member activity

+
+ +
+ +
+
+ +
\ No newline at end of file diff --git a/ghost/admin/app/components/gh-member-settings-form-cp.js b/ghost/admin/app/components/gh-member-settings-form-cp.js new file mode 100644 index 0000000000..15e58c3c71 --- /dev/null +++ b/ghost/admin/app/components/gh-member-settings-form-cp.js @@ -0,0 +1,107 @@ +import Component from '@ember/component'; +import moment from 'moment'; +import {action} from '@ember/object'; +import {computed} from '@ember/object'; +import {gt} from '@ember/object/computed'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; + +export default Component.extend({ + membersUtils: service(), + feature: service(), + config: service(), + mediaQueries: service(), + ghostPaths: service(), + ajax: service(), + store: service(), + + stripeDetailsType: 'subscription', + + // Allowed actions + setProperty: () => {}, + + hasMultipleSubscriptions: gt('member.subscriptions', 1), + + canShowStripeInfo: computed('member.isNew', 'membersUtils.isStripeEnabled', function () { + let stripeEnabled = this.membersUtils.isStripeEnabled; + + if (this.member.get('isNew') || !stripeEnabled) { + return false; + } else { + return true; + } + }), + + subscriptions: computed('member.subscriptions', function () { + let subscriptions = this.member.get('subscriptions'); + if (subscriptions && subscriptions.length > 0) { + return subscriptions.map((subscription) => { + const statusLabel = subscription.status ? subscription.status.replace('_', ' ') : ''; + return { + id: subscription.id, + customer: subscription.customer, + name: subscription.name || '', + email: subscription.email || '', + status: subscription.status, + statusLabel: statusLabel, + startDate: subscription.start_date ? moment(subscription.start_date).format('D MMM YYYY') : '-', + plan: subscription.plan, + amount: parseInt(subscription.plan.amount) ? (subscription.plan.amount / 100) : 0, + cancelAtPeriodEnd: subscription.cancel_at_period_end, + cancellationReason: subscription.cancellation_reason, + validUntil: subscription.current_period_end ? moment(subscription.current_period_end).format('D MMM YYYY') : '-' + }; + }).reverse(); + } + return null; + }), + + customer: computed('subscriptions.[]', function () { + let customer = this.subscriptions.firstObject?.customer; + if (customer) { + return Object.assign({}, this.subscriptions.firstObject?.customer, { + startDate: this.subscriptions.firstObject?.startDate + }); + } + return null; + }), + + actions: { + setProperty(property, value) { + this.setProperty(property, value); + }, + setLabels(labels) { + this.member.set('labels', labels); + } + }, + + changeStripeDetailsType: action(function (type) { + this.set('stripeDetailsType', type); + }), + + cancelSubscription: task(function* (subscriptionId) { + let url = this.get('ghostPaths.url').api('members', this.member.get('id'), 'subscriptions', subscriptionId); + + let response = yield this.ajax.put(url, { + data: { + cancel_at_period_end: true + } + }); + + this.store.pushPayload('member', response); + return response; + }).drop(), + + continueSubscription: task(function* (subscriptionId) { + let url = this.get('ghostPaths.url').api('members', this.member.get('id'), 'subscriptions', subscriptionId); + + let response = yield this.ajax.put(url, { + data: { + cancel_at_period_end: false + } + }); + + this.store.pushPayload('member', response); + return response; + }).drop() +}); diff --git a/ghost/admin/app/styles/components/lists.css b/ghost/admin/app/styles/components/lists.css index dfbae7bf0a..ada9e835d7 100644 --- a/ghost/admin/app/styles/components/lists.css +++ b/ghost/admin/app/styles/components/lists.css @@ -57,11 +57,15 @@ ul.nostyle li { font-weight: 500; letter-spacing: 0.1px; color: var(--black); - text-transform: uppercase; padding: 10px 20px; + text-transform: uppercase; white-space: nowrap; } +.gh-list-row.header.empty .gh-list-header { + padding: 0; +} + .gh-list:not(.tabbed) .gh-list-header:first-child { border-top-left-radius: 5px; padding-left: 0; diff --git a/ghost/admin/app/styles/layouts/main.css b/ghost/admin/app/styles/layouts/main.css index 5ece60d263..4f694aafa1 100644 --- a/ghost/admin/app/styles/layouts/main.css +++ b/ghost/admin/app/styles/layouts/main.css @@ -1155,15 +1155,14 @@ grid-column-gap: 24px; } - -.gh-main-section-content.grey { - background: var(--main-color-content-greybg); -} - .gh-main-section-content.padding-top-s { padding-top: 16px; } +.gh-main-section-content + .gh-main-section-header { + margin-top: 24px; +} + .gh-main-section-block.stretch-height { height: 100%; display: flex; diff --git a/ghost/admin/app/styles/layouts/members.css b/ghost/admin/app/styles/layouts/members.css index b73a3d14d6..73bfcbf659 100644 --- a/ghost/admin/app/styles/layouts/members.css +++ b/ghost/admin/app/styles/layouts/members.css @@ -1533,4 +1533,16 @@ p.gh-members-import-errordetail:first-of-type { .gh-members-emailpreview-container { transform: scale(0.9); } +} + + +/* Custom product member details */ +.gh-cp-member-email-name { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 24px; +} + +.gh-cp-data-summary:not(:last-of-type) { + margin-bottom: 24px; } \ No newline at end of file diff --git a/ghost/admin/app/templates/member.hbs b/ghost/admin/app/templates/member.hbs index ffa493d47f..f104b2e0ad 100644 --- a/ghost/admin/app/templates/member.hbs +++ b/ghost/admin/app/templates/member.hbs @@ -27,11 +27,19 @@
- + {{#if (enable-developer-experiments)}} + + {{else}} + + {{/if}} {{#unless this.member.isNew}}