mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 07:09:48 +03:00
b5bc870a3b
refs https://github.com/TryGhost/Team/issues/986#issuecomment-900281186 The tiers features updated the "comped" functionality to no longer create a Stripe Subscription for members. Since theme developers expect comped members will have a subscription, we added a dummy subscription object for comped members in API. Since Admin previously expected comped members to be ones without any subscription but access to product, this change updates Admin behavior to identify comped subscriptions on a product and show them the same way as before.
352 lines
22 KiB
Handlebars
352 lines
22 KiB
Handlebars
<div class="gh-member-settings" ...attributes>
|
||
|
||
<section class="gh-main-section no-heading gh-member-detail-overview">
|
||
<div class="gh-main-section-block">
|
||
<div class="gh-main-section-content">
|
||
<div class="flex items-center">
|
||
{{#if (or this.member.name this.member.email)}}
|
||
<GhMemberAvatar
|
||
@member={{this.member}}
|
||
@sizeClass={{if this.member.name 'f-subheadline fw4 lh-zero tracked-1' 'f-subheadline fw4 lh-zero tracked-1'}}
|
||
@containerClass="w20 h20 mr4 gh-member-detail-avatar"
|
||
/>
|
||
{{else}}
|
||
<div class="flex items-center justify-center br-100 mr4 gh-new-member-avatar">
|
||
<span class="gh-member-avatar-label f-subheadline fw4 lh-zero tracked-1">N</span>
|
||
</div>
|
||
{{/if}}
|
||
<div>
|
||
<h3>
|
||
{{or this.member.name this.member.email}}
|
||
{{#unless (or this.member.name this.member.email)}}
|
||
{{#if this.member.isNew}}
|
||
<span class="midgrey">New member</span>
|
||
{{/if}}
|
||
{{/unless}}
|
||
</h3>
|
||
<p>
|
||
{{#if (and this.member.name this.member.email)}}
|
||
<a href="mailto:{{this.member.email}}">{{this.member.email}}</a>
|
||
{{/if}}
|
||
</p>
|
||
{{#unless this.member.isNew}}
|
||
<p class="{{if this.member.name "nudge-bottom--2"}}">
|
||
{{#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"}}
|
||
</p>
|
||
{{/unless}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="gh-main-section columns-3">
|
||
<div class="gh-main-section-block span-2">
|
||
<div class="gh-main-section-content grey">
|
||
<div>
|
||
<div class="gh-cp-member-email-name">
|
||
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="name" @classNames="max-width">
|
||
<label for="member-name">Name</label>
|
||
<GhTextInput @id="member-name" @name="name" @value={{this.scratchMember.name}} @tabindex="1"
|
||
@focus-out={{action "setProperty" "name" this.scratchMember.name}} data-test-input="member-name" />
|
||
<GhErrorMessage @errors={{member.errors}} @property="name" />
|
||
</GhFormGroup>
|
||
|
||
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="email" @classNames="max-width">
|
||
<label for="member-email">Email</label>
|
||
<GhTextInput @value={{this.scratchMember.email}} @id="member-email" @name="email" @tabindex="2"
|
||
@autocapitalize="off" @autocorrect="off" @autocomplete="off"
|
||
@focus-out={{action "setProperty" "email" this.scratchMember.email}} data-test-input="member-email"/>
|
||
<GhErrorMessage @errors={{this.member.errors}} @property="email" />
|
||
</GhFormGroup>
|
||
</div>
|
||
|
||
<GhFormGroup @classNames="gh-member-labels">
|
||
<label for="label-input">Labels</label>
|
||
<GhMemberLabelInput @onChange={{action "setLabels"}} @labels={{this.member.labels}} @triggerId="label-input" data-test-input="" />
|
||
</GhFormGroup>
|
||
|
||
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="note" @classNames="mb0 gh-member-note">
|
||
<label for="member-note">Note <span class="midgrey-l2 fw4">(not visible to member)</span></label>
|
||
<GhTextarea @id="member-note" @name="note" @class="gh-member-details-textarea" @tabindex="3"
|
||
@value={{this.scratchMember.note}} @focus-out={{action "setProperty" "note" this.scratchMember.note}} data-test-input="member-note" />
|
||
<GhErrorMessage @errors={{this.member.errors}} @property="note" />
|
||
<p> Maximum: <b>500</b> characters. You’ve used
|
||
{{gh-count-down-characters this.scratchMember.note 500}}</p>
|
||
</GhFormGroup>
|
||
|
||
<GhFormGroup @classNames="gh-members-subscribed-checkbox mb0">
|
||
<div class="flex justify-between items-center">
|
||
<div>
|
||
<h4 class="gh-setting-title m">Subscribed to newsletter</h4>
|
||
<p class="gh-setting-desc">If disabled, member will <em>not</em> receive newsletter emails</p>
|
||
</div>
|
||
<div class="for-switch">
|
||
<label class="switch" for="subscribed-checkbox">
|
||
<Input @checked={{this.member.subscribed}} @type="checkbox" @id="subscribed-checkbox"
|
||
@name="subscribed" data-test-checkbox="member-subscribed" />
|
||
<span class="input-toggle-component"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</GhFormGroup>
|
||
</div>
|
||
</div>
|
||
|
||
<h4 class="gh-main-section-header small bn">Subscriptions</h4>
|
||
|
||
{{#unless this.products}}
|
||
<div class="gh-main-section-content bordered">
|
||
<div class="gh-cp-memberproduct-noproduct">
|
||
{{#unless this.isCreatingComplimentary}}
|
||
<div class="mb2">This member doesn't have subscriptions.</div>
|
||
{{/unless}}
|
||
|
||
{{#unless this.member.isNew}}
|
||
{{#if this.isAddComplimentaryAllowed}}
|
||
{{#if this.isCreatingComplimentary}}
|
||
<GhLoadingSpinner />
|
||
{{else}}
|
||
{{#if (feature "multipleProducts")}}
|
||
{{!-- {{if has multiple products!}} --}}
|
||
<button type="button" class="gh-btn gh-btn-text green gh-btn-icon gh-btn-addproduct" {{action (toggle "showMemberProductModal" this)}}>
|
||
<span>{{svg-jar "add"}} Add complimentary subscription</span>
|
||
</button>
|
||
{{!-- {{/if}} --}}
|
||
{{else}}
|
||
<button type="button" class="gh-btn gh-btn-text green gh-btn-icon gh-btn-addproduct" {{action "addCompedSubscription"}}>
|
||
<span>{{svg-jar "add"}} Add complimentary subscription</span>
|
||
</button>
|
||
{{/if}}
|
||
{{/if}}
|
||
{{/if}}
|
||
{{/unless}}
|
||
</div>
|
||
</div>
|
||
{{/unless}}
|
||
|
||
{{#each this.products as |product|}}
|
||
<div class="gh-member-product-container">
|
||
<div class="gh-cp-memberproduct {{if (gt product.subscriptions.length 1) "multiple-subs" ""}}">
|
||
<h3 class="gh-memberproduct-name">
|
||
{{product.name}}
|
||
{{#if (gt product.subscriptions.length 1)}}
|
||
<span class="gh-memberproduct-subcount">{{product.subscriptions.length}} subscriptions</span>
|
||
{{/if}}
|
||
</h3>
|
||
|
||
{{#each product.subscriptions as |sub|}}
|
||
{{#if sub.isComplimentary}}
|
||
<div class="gh-memberproduct-subscription">
|
||
<div>
|
||
<div>
|
||
<span class="gh-cp-memberproduct-pricelabel">Complimentary</span>
|
||
<span class="gh-badge active">Active</span>
|
||
</div>
|
||
<div class="gh-memberproduct-created">Created on</div>
|
||
</div>
|
||
<div class="flex items-center">
|
||
<div class="gh-product-card-price">
|
||
<div class="flex items-start">
|
||
<span class="currency-symbol">$</span>
|
||
<span class="amount">0</span>
|
||
</div>
|
||
<div class="period">yearly</div>
|
||
</div>
|
||
<span class="action-menu">
|
||
<GhDropdownButton @dropdownName="subscription-menu-complimentary" @classNames="gh-btn gh-btn-outline gh-btn-icon gh-btn-subscription-action icon-only" @title="Actions">
|
||
<span>
|
||
{{svg-jar "dotdotdot"}}
|
||
<span class="hidden">Subscription menu</span>
|
||
</span>
|
||
</GhDropdownButton>
|
||
<GhDropdown @name="subscription-menu-complimentary" @tagName="ul" @classNames="product-actions-menu dropdown-menu dropdown-align-right">
|
||
<li>
|
||
<button {{action "removeComplimentary" product.id}}>
|
||
<span class="red">Remove complimentary subscription</span>
|
||
</button>
|
||
</li>
|
||
</GhDropdown>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{{else}}
|
||
<div class="gh-memberproduct-subscription">
|
||
<div>
|
||
<div>
|
||
<span class="gh-cp-memberproduct-pricelabel">{{sub.price.nickname}}</span>
|
||
–
|
||
{{#if sub.cancel_at_period_end}}
|
||
<span class="gh-cp-memberproduct-renewal">Has access until {{sub.validUntil}}</span>
|
||
<span class="gh-badge archived">Cancelled</span>
|
||
{{else}}
|
||
<span class="gh-cp-memberproduct-renewal">Renews {{sub.validUntil}}</span>
|
||
<span class="gh-badge active">Active</span>
|
||
{{/if}}
|
||
</div>
|
||
{{#if sub.cancellationReason}}
|
||
<div class="gh-memberproduct-cancelreason"><span class="fw6">Cancellation reason:</span> {{sub.cancellationReason}}</div>
|
||
{{/if}}
|
||
<div class="gh-memberproduct-created">Created on {{sub.startDate}}</div>
|
||
</div>
|
||
|
||
<div class="flex items-center">
|
||
<div class="gh-product-card-price">
|
||
<div class="flex items-start">
|
||
<span class="currency-symbol">{{sub.price.currencySymbol}}</span>
|
||
<span class="amount">{{sub.price.nonDecimalAmount}}</span>
|
||
</div>
|
||
<div class="period">{{if (eq sub.price.interval "year") "yearly" "monthly"}}</div>
|
||
</div>
|
||
|
||
<span class="action-menu">
|
||
<GhDropdownButton @dropdownName="subscription-menu-{{sub.id}}" @classNames="gh-btn gh-btn-outline gh-btn-icon gh-btn-subscription-action icon-only" @title="Actions">
|
||
<span>
|
||
{{svg-jar "dotdotdot"}}
|
||
<span class="hidden">Subscription menu</span>
|
||
</span>
|
||
</GhDropdownButton>
|
||
<GhDropdown @name="subscription-menu-{{sub.id}}" @tagName="ul" @classNames="product-actions-menu dropdown-menu dropdown-align-right">
|
||
<li>
|
||
<a href="https://dashboard.stripe.com/customers/{{sub.customer.id}}" target="_blank" rel="noopener">
|
||
View Stripe customer
|
||
</a>
|
||
</li>
|
||
<li class="divider"></li>
|
||
<li>
|
||
<a href="https://dashboard.stripe.com/subscriptions/{{sub.id}}" target="_blank" rel="noopener">
|
||
View Stripe subscription
|
||
</a>
|
||
</li>
|
||
<li>
|
||
{{#if sub.cancel_at_period_end}}
|
||
<button {{action "continueSubscription" sub.id}}>
|
||
<span>Continue subscription</span>
|
||
</button>
|
||
{{else}}
|
||
<button {{action "cancelSubscription" sub.id}}>
|
||
<span class="red">Cancel subscription</span>
|
||
</button>
|
||
{{/if}}
|
||
</li>
|
||
</GhDropdown>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{{/if}}
|
||
{{/each}}
|
||
|
||
{{#if (and (feature "multipleProducts") (eq product.subscriptions.length 0))}}
|
||
<div class="gh-memberproduct-subscription">
|
||
<div>
|
||
<div>
|
||
<span class="gh-cp-memberproduct-pricelabel">Complimentary</span>
|
||
<span class="gh-badge active">Active</span>
|
||
</div>
|
||
<div class="gh-memberproduct-created">Created on</div>
|
||
</div>
|
||
<div class="flex items-center">
|
||
<div class="gh-product-card-price">
|
||
<div class="flex items-start">
|
||
<span class="currency-symbol">$</span>
|
||
<span class="amount">0</span>
|
||
</div>
|
||
<div class="period">yearly</div>
|
||
</div>
|
||
<span class="action-menu">
|
||
<GhDropdownButton @dropdownName="subscription-menu-complimentary" @classNames="gh-btn gh-btn-outline gh-btn-icon gh-btn-subscription-action icon-only" @title="Actions">
|
||
<span>
|
||
{{svg-jar "dotdotdot"}}
|
||
<span class="hidden">Subscription menu</span>
|
||
</span>
|
||
</GhDropdownButton>
|
||
<GhDropdown @name="subscription-menu-complimentary" @tagName="ul" @classNames="product-actions-menu dropdown-menu dropdown-align-right">
|
||
<li>
|
||
<button {{action "removeComplimentary" product.id}}>
|
||
<span class="red">Remove complimentary subscription</span>
|
||
</button>
|
||
</li>
|
||
</GhDropdown>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{{/if}}
|
||
|
||
{{#if (not (feature "multipleProducts"))}}
|
||
{{#if this.isAddComplimentaryAllowed}}
|
||
<div class="gh-memberproduct-list-footer bt b--whitegrey pt2 {{if this.isCreatingComplimentary "min-height" ""}}">
|
||
{{#if this.isCreatingComplimentary}}
|
||
<GhLoadingSpinner />
|
||
{{else}}
|
||
<button type="button" class="gh-btn gh-btn-text green gh-btn-icon gh-btn-addproduct" {{action "addCompedSubscription"}}>
|
||
<span>{{svg-jar "add"}} Add complimentary subscription</span>
|
||
</button>
|
||
{{/if}}
|
||
</div>
|
||
{{/if}}
|
||
{{/if}}
|
||
</div>
|
||
</div>
|
||
{{/each}}
|
||
{{#if (feature "multipleProducts")}}
|
||
{{#if (and this.products this.isAddComplimentaryAllowed)}}
|
||
<div class="gh-memberproduct-list-footer {{if this.isCreatingComplimentary "min-height" ""}}">
|
||
{{#if this.isCreatingComplimentary}}
|
||
<GhLoadingSpinner />
|
||
{{else}}
|
||
<button
|
||
type="button"
|
||
class="gh-btn gh-btn-text green gh-btn-icon gh-btn-addproduct"
|
||
{{action (toggle "showMemberProductModal" this)}}
|
||
>
|
||
<span>{{svg-jar "add"}} Add complimentary subscription</span>
|
||
</button>
|
||
{{/if}}
|
||
</div>
|
||
{{/if}}
|
||
{{/if}}
|
||
</div>
|
||
<div class="gh-main-section-block">
|
||
<div class="gh-main-section-content bordered">
|
||
<div class="gh-heading-xs">Emails received</div>
|
||
<div class="gh-data-summary gh-cp-data-summary">{{@member.emailCount}}</div>
|
||
<div class="gh-heading-xs">Emails opened</div>
|
||
<div class="gh-data-summary gh-cp-data-summary">{{@member.emailOpenedCount}}</div>
|
||
<div class="gh-heading-xs">Avg. open rate</div>
|
||
<div class="gh-data-summary gh-cp-data-summary">
|
||
{{#if (is-empty @member.emailOpenRate)}}
|
||
<span data-tooltip="Insufficient data available">–</span>
|
||
{{else}}
|
||
{{@member.emailOpenRate}}%
|
||
{{/if}}
|
||
</div>
|
||
</div>
|
||
|
||
<h4 class="gh-main-section-header small bn">Member activity</h4>
|
||
<div class="gh-main-section-content bordered">
|
||
<GhMemberActivityFeed @emailRecipients={{this.member.emailRecipients}} />
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
{{#if this.showMemberProductModal}}
|
||
<GhFullscreenModal @modifier="action wide member-product">
|
||
<ModalMemberProduct
|
||
@model={{this.member}}
|
||
@confirm={{this.addProduct}}
|
||
@closeModal={{this.closeMemberProductModal}} />
|
||
</GhFullscreenModal>
|
||
{{/if}}
|