Moved Stripe connect settings to modal

This commit is contained in:
Peter Zimon 2021-05-17 17:14:38 +02:00
parent 35612db851
commit fa95868c36
10 changed files with 372 additions and 289 deletions

View File

@ -16,7 +16,7 @@ const FullScreenModalComponent = Component.extend({
return `modal-${this.modal || 'unknown'}`;
}),
modalClasses: computed('modifiers', function () {
modalClasses: computed('modifier', function () {
let modalClass = 'fullscreen-modal';
let modifiers = (this.modifier || '').split(' ');
let modalClasses = emberA([modalClass]);

View File

@ -1,188 +1,125 @@
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn"></h4>
<section class="gh-expandable">
{{#if this.stripeDirect}}
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Connect to Stripe</h4>
<p class="gh-expandable-description">Configure API keys to create subscriptions and take payments</p>
{{#if this.stripeDirect}}
<section>
<div class="flex flex-column flex-row-l items-start justify-between">
<div class="w-100 w-50-l">
<div class="mb4">
<label class="fw6 f8">Stripe Publishable key</label>
<GhTextInput
@type="password"
@value={{readonly this.stripeDirectPublicKey}}
@input={{action "setStripeDirectPublicKey"}}
@class="mt1 password"
/>
</div>
<div class="nudge-top--3">
<label class="fw6 f8 mt4">Stripe Secret key</label>
<GhTextInput
@type="password"
@value={{readonly this.stripeDirectSecretKey}}
@input={{action "setStripeDirectSecretKey"}}
@class="mt1 password"
/>
<a href="https://dashboard.stripe.com/account/apikeys" target="_blank" class="mt1 fw4 f8">
Find your Stripe API keys here &raquo;
</a>
</div>
<button type="button" class="gh-btn" {{action (toggle "membersStripeOpen" this)}} data-test-toggle-membersstripe><span>{{if this.membersStripeOpen "Close" "Expand"}}</span></button>
</div>
<div class="gh-expandable-content">
{{#liquid-if this.membersStripeOpen}}
<div class="flex flex-column flex-row-l items-start justify-between mb4 mt6">
<div class="w-100 w-50-l">
<div class="mb4">
<label class="fw6 f8">Stripe Publishable key</label>
<GhTextInput
@type="password"
@value={{readonly this.stripeDirectPublicKey}}
@input={{action "setStripeDirectPublicKey"}}
@class="mt1 password"
/>
</div>
<div class="nudge-top--3">
<label class="fw6 f8 mt4">Stripe Secret key</label>
<GhTextInput
@type="password"
@value={{readonly this.stripeDirectSecretKey}}
@input={{action "setStripeDirectSecretKey"}}
@class="mt1 password"
/>
<a href="https://dashboard.stripe.com/account/apikeys" target="_blank" class="mt1 fw4 f8">
Find your Stripe API keys here &raquo;
</a>
</div>
</div>
<div class="ml0 ml5-l mt6">
<div class="gh-members-stripe-info">
<div class="gh-members-stripe-info-header">
<h4>How you get paid</h4>
{{svg-jar "stripe-verified-partner-badge" class="gh-members-stripe-badge"}}
</div>
<p class="f8 mt2 mb0">
Stripe is our exclusive direct payments partner.<br />
Ghost collects <strong>no fees</strong> on any payments! If you dont have a Stripe account yet, you can <a href="https://stripe.com" target="_blank" rel="noopener" class="gh-members-stripe-link">sign up here</a>.
</p>
</div>
</div>
<div class="ml0 ml5-l mt6">
<div class="gh-members-stripe-info">
<div class="gh-members-stripe-info-header">
<h4>How you get paid</h4>
{{svg-jar "stripe-verified-partner-badge" class="gh-members-stripe-badge"}}
</div>
{{/liquid-if}}
</div>
</div>
{{else}}
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Connect to Stripe</h4>
<p class="gh-expandable-description">
{{#if this.stripeConnectAccountId}}
{{#if this.hasActiveStripeSubscriptions}}
<span class="red">
Cannot disconnect while there are members with active Stripe subscriptions.
</span>
{{else}}
<span>
{{#if this.stripeConnectSuccess}}
{{svg-jar "check-circle" class="stroke-green w4 h4 nudge-top--3"}} <span class="green-d1">Successfully connected to {{this.stripeConnectAccountName}}</span>
{{else}}
Connected to <a href="https://dashboard.stripe.com/{{this.stripeConnectAccountId}}" target="_blank">{{this.stripeConnectAccountName}}</a>
{{/if}}
{{#unless this.stripeConnectLivemode}}
<span class="gh-members-connect-testmodelabel">Test mode</span>
{{/unless}}
</span>
{{/if}}
{{else}}
<span>Connect to Stripe to create subscriptions and take payments</span>
{{/if}}
<p class="f8 mt2 mb0">
Stripe is our exclusive direct payments partner.<br />
Ghost collects <strong>no fees</strong> on any payments! If you dont have a Stripe account yet, you can <a href="https://stripe.com" target="_blank" rel="noopener" class="gh-members-stripe-link">sign up here</a>.
</p>
</div>
{{#if this.stripeConnectAccountId}}
<button type="button" class="gh-btn" {{action "openDisconnectStripeModal"}}><span>Disconnect</span></button>
{{else}}
<button type="button" class="gh-btn" {{action (toggle "membersStripeOpen" this)}} data-test-toggle-membersstripe><span>{{if this.membersStripeOpen "Close" "Expand"}}</span></button>
</div>
</div>
</section>
<div class="mb4 mt4 flex justify-end">
<GhTaskButton @buttonText="Save settings"
@task={{this.saveSettings}}
@successText="Saved"
@runningText="Saving"
@class="gh-btn gh-btn-primary gh-btn-icon"
data-test-button="save-members-settings"
/>
</div>
{{else}}
{{#if this.stripeConnectAccountId}}
<div class="gh-stripe-connected-container">
{{svg-jar "check-circle-stroke" class="check-circle"}}
<h1>You are connected to Stripe</h1>
<div class="gh-stripe-connected-info">
<p>Connected to <a href="https://dashboard.stripe.com/{{this.stripeConnectAccountId}}" target="_blank">{{this.stripeConnectAccountName}}</a></p>
{{#unless this.stripeConnectLivemode}}
<div class="gh-members-connect-testmodelabel">Test mode</div>
{{/unless}}
{{#if this.hasActiveStripeSubscriptions}}
<span class="gh-stripe-error-hasactivesub">
Cannot disconnect while there are members with active Stripe subscriptions.
</span>
{{/if}}
</div>
<div class="gh-expandable-content">
{{#liquid-if this.membersStripeOpen}}
<div class="mb2">
<div class="flex flex-column flex-row-l items-start justify-between">
<div class="w-100 w-50-l">
<label class="fw6 f8">Generate secure key</label>
<div class="flex items-center mb4 justify-between gh-members-connectbutton-container mt2">
<a href="{{if this.stripeConnectTestMode this.testStripeConnectAuthUrl this.liveStripeConnectAuthUrl}}" class="stripe-connect light-blue" target="_blank"><span>Connect with Stripe</span></a>
<div class="ml2 flex items-center flex-nowrap">
<span class="mr2 f8 midgrey nowrap {{if this.stripeConnectTestMode "gh-members-connect-testmodeon"}}">{{if this.stripeConnectTestMode "Using" "Use"}} test mode</span>
<div class="for-switch small">
<label class="switch" for="stripe-connect-test-mode" {{action (toggle "stripeConnectTestMode" this) bubbles="false"}}>
<input type="checkbox" class="gh-input" checked={{this.stripeConnectTestMode}} onclick={{action (toggle "stripeConnectTestMode" this)}} data-test-checkbox="stripe-connect-test-mode">
<span class="input-toggle-component mt1"></span>
</label>
</div>
</div>
</div>
<div class="nudge-top--3">
<GhTextarea
@class="gh-members-stripe-connect-token"
@placeholder="Paste your secure key here"
@input={{action "setStripeConnectIntegrationToken"}}
/>
{{#if this.stripeConnectError}}<p class="mb0 mt2 f8 red">{{this.stripeConnectError}}</p>{{/if}}
</div>
<button type="button" class="gh-btn gh-btn-stripe-disconnect" {{action "openDisconnectStripeModal"}}><span>Disconnect</span></button>
</div>
{{else}}
<div class="flex flex-column flex-row-l items-start justify-between">
<div class="w-100 w-50-l">
<label class="fw6 f8">Generate secure key</label>
<div class="flex items-center mb4 justify-between gh-members-connectbutton-container mt2">
<a href="{{if this.stripeConnectTestMode this.testStripeConnectAuthUrl this.liveStripeConnectAuthUrl}}" class="stripe-connect" target="_blank"><span>Connect with Stripe</span></a>
<div class="ml2 flex items-center flex-nowrap">
<span class="mr2 f8 midgrey nowrap {{if this.stripeConnectTestMode "gh-members-connect-testmodeon"}}">{{if this.stripeConnectTestMode "Using" "Use"}} test mode</span>
<div class="for-switch small">
<label class="switch" for="stripe-connect-test-mode" {{action (toggle "stripeConnectTestMode" this) bubbles="false"}}>
<input type="checkbox" class="gh-input" checked={{this.stripeConnectTestMode}} onclick={{action (toggle "stripeConnectTestMode" this)}} data-test-checkbox="stripe-connect-test-mode">
<span class="input-toggle-component mt1"></span>
</label>
</div>
<div class="mt5 mt5-m mt8-l ml0 ml5-l">
<div class="gh-members-stripe-info">
<div class="gh-members-stripe-info-header">
<h4>How you get paid</h4>
{{svg-jar "stripe-verified-partner-badge" class="gh-members-stripe-badge"}}
</div>
<p class="f8 mt2 mb0">
Stripe is our exclusive direct payments partner.<br />
Ghost collects <strong>no fees</strong> on any payments! If you dont have a Stripe account yet, you can <a href="https://stripe.com" target="_blank" rel="noopener" class="gh-members-stripe-link">sign up here</a>.
</p>
</div>
</div>
</div>
<div class="gh-members-connect-savecontainer {{if this.settings.stripeConnectIntegrationToken "expanded"}}">
<GhTaskButton @buttonText="Save Stripe settings"
@task={{this.saveStripeSettings}}
@successText="Saved"
@disabled={{is-empty this.settings.stripeConnectIntegrationToken}}
@runningText="Saving"
@class="gh-btn gh-btn-green gh-btn-icon"
/>
</div>
</div>
{{/liquid-if}}
<div class="nudge-top--3">
<GhTextarea
@class="gh-members-stripe-connect-token"
@placeholder="Paste your secure key here"
@input={{action "setStripeConnectIntegrationToken"}}
/>
{{#if this.stripeConnectError}}<p class="mb0 mt2 f8 red">{{this.stripeConnectError}}</p>{{/if}}
</div>
</div>
<div class="mt5 mt5-m mt8-l ml0 ml5-l">
<div class="gh-members-stripe-info">
<div class="gh-members-stripe-info-header">
<h4>Getting paid</h4>
{{svg-jar "stripe-verified-partner-badge" class="gh-members-stripe-badge"}}
</div>
<p class="f8 mt2 mb0">
Stripe is our exclusive direct payments partner.<br />
Ghost collects <strong>no fees</strong> on any payments! If you dont have a Stripe account yet, you can <a href="https://stripe.com" target="_blank" rel="noopener" class="gh-members-stripe-link">sign up here</a>.
</p>
</div>
</div>
</div>
<div class="gh-members-connect-savecontainer {{if this.settings.stripeConnectIntegrationToken "expanded"}}">
<GhTaskButton @buttonText="Save Stripe settings"
@task={{this.saveStripeSettings}}
@successText="Saved"
@disabled={{is-empty this.settings.stripeConnectIntegrationToken}}
@runningText="Saving"
@class="gh-btn gh-btn-green gh-btn-icon"
/>
</div>
{{/if}}
{{!-- <div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Subscription pricing</h4>
<p class="gh-expandable-description">Set default subscription currency</p>
</div>
<button type="button" class="gh-btn" {{action (toggle "membersPricingOpen" this)}} data-test-toggle-memberspricing><span>{{if this.membersPricingOpen "Close" "Expand"}}</span></button>
</div>
<div class="gh-expandable-content">
{{#liquid-if this.membersPricingOpen}}
<div class="w-100 w-50-l flex flex-column flex-row-ns">
<div class="w-100">
<GhFormGroup @class="for-select">
<label class="fw6 f8"for="currency">Default currency</label>
<span class="gh-select mt1">
{{one-way-select this.selectedCurrency
id="currency"
name="currency"
options=(readonly this.allCurrencies)
optionValuePath="value"
optionLabelPath="label"
update=(action "setStripePlansCurrency")
}}
{{svg-jar "arrow-down-small"}}
</span>
</GhFormGroup>
</div>
</div>
<div class="w-100 w-50-l flex flex-column flex-row-ns">
<GhErrorMessage @errors={{settings.errors}} @property="stripePlans" class="w-100 red"/>
</div>
{{/liquid-if}}
</div>
</div> --}}
</section>
{{/if}}
</div>
{{#if this.showDisconnectStripeConnectModal}}

View File

@ -248,6 +248,10 @@ export default Component.extend({
}
}).drop(),
saveSettings: task(function* () {
return yield this.settings.save();
}).drop(),
get liveStripeConnectAuthUrl() {
return this.ghostPaths.url.api('members/stripe_connect') + '?mode=live';
},

View File

@ -0,0 +1,30 @@
{{#unless this.settings.stripeConnectAccountId}}
<header class="modal-header" data-test-modal="webhook-form">
<h1 data-test-text="title">Connect with Stripe</h1>
</header>
{{/unless}}
<button class="close" href title="Close" {{action "closeModal"}} {{action (optional this.noop) on="mouseDown"}}>
{{svg-jar "close"}}
</button>
<form>
<div class="modal-body">
<GhMembersPaymentsSetting
@setDefaultContentVisibility={{this.setDefaultContentVisibility}}
@setStripeConnectIntegrationTokenSetting={{this.setStripeConnectIntegrationTokenSetting}}
/>
</div>
</form>
<div class="modal-footer">
{{#if this.settings.stripeConnectAccountId}}
<button
class="gh-btn gh-btn-black"
{{action "closeModal"}}
{{action (optional this.noop) on="mouseDown"}}
data-test-button="cancel-webhook"
>
<span>OK</span>
</button>
{{/if}}
</div>

View File

@ -0,0 +1,21 @@
import ModalBase from 'ghost-admin/components/modal-base';
import classic from 'ember-classic-decorator';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';
// TODO: update modals to work fully with Glimmer components
@classic
export default class ModalStripeConnect extends ModalBase {
@service settings;
@action
setDefaultContentVisibility(value) {
this.settings.set('defaultContentVisibility', value);
}
@action
setStripeConnectIntegrationTokenSetting(stripeConnectIntegrationToken) {
this.settings.set('stripeConnectIntegrationToken', stripeConnectIntegrationToken);
}
}

View File

@ -13,6 +13,7 @@ export default class MembersAccessController extends Controller {
@tracked showLeavePortalModal = false;
@tracked showLeaveRouteModal = false;
@tracked showPortalSettings = false;
@tracked showStripeConnect = false;
@tracked product = null;
@tracked stripePrices = [];
@ -112,6 +113,16 @@ export default class MembersAccessController extends Controller {
}
}
openStripeSettings() {
// Open stripe settings here
}
@action
closeStripeConnect() {
this.showStripeConnect = false;
}
@action
closePortalSettings() {
const changedAttributes = this.settings.changedAttributes();

View File

@ -64,119 +64,6 @@
opacity: 0.25;
}
/* Stripe settings */
.gh-members-stripe-info-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.gh-members-stripe-info-header h4 {
font-weight: 600;
margin: 0;
padding: 0;
}
.gh-members-stripe-info {
border-radius: 0.9rem;
border: 1px solid var(--whitegrey);
background: var(--white);
padding: 12px;
width: 380px;
}
.gh-members-stripe-badge {
width: 180px;
}
.gh-members-stripe-link {
color: #555ABF;
}
.gh-members-connectbutton-container {
max-width: 380px;
}
.gh-members-connectbutton-container .for-switch {
line-height: 1em;
}
.gh-members-connectbutton-container .for-switch label {
width: 36px !important
}
.gh-members-connectbutton-container .for-switch input:checked + .input-toggle-component {
background: #F1946A;
}
.gh-members-connect-testmodeon {
color: #F1946A;
}
.gh-members-stripe-connect-token {
background: var(--whitegrey-l2);
min-height: unset;
height: 73px;
max-width: 380px;
font-family: var(--font-family-mono);
font-size: 1.3rem;
resize: none;
}
.gh-members-connect-testmodelabel {
display: inline-block;
background: #f8e5b9;
color: #983705;
font-size: 1.2rem;
font-weight: 500;
line-height: 1em;
border-radius: 999px;
padding: 4px 8px;
}
.gh-members-connect-savecontainer {
height: 0px;
overflow-y: hidden;
transition: all 0.2s ease-in-out;
opacity: 0;
margin-top: 16px;
margin-bottom: 0;
}
.gh-members-connect-savecontainer.expanded {
margin-bottom: 20px;
}
.gh-members-connect-savecontainer.expanded {
height: 36px;
opacity: 1.0;
}
@media (max-width: 500px) {
.gh-members-stripe-info-header {
flex-direction: column;
align-items: stretch;
}
.gh-members-stripe-info-header h4 {
order: 2;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--whitegrey);
}
.gh-members-stripe-badge {
order: 1;
/* margin: -10px 0 0 -10px; */
}
.gh-members-stripe-info {
width: 100%;
}
}
.gh-labs-members-emaildropdown {
min-width: 208px;
margin-left: 8px;

View File

@ -1427,7 +1427,7 @@ p.theme-validation-details {
}
.gh-setting-members-tierscontainer {
margin-top: 3vmin;
margin-top: 7vmin;
}
.gh-settings-members-tiersheader {
@ -1506,6 +1506,182 @@ p.theme-validation-details {
;
width: 420px;
height: 582px;
margin-bottom: 60px;
margin-bottom: 32px;
border-radius: 5px;
}
/* Stripe Connect modal */
.fullscreen-modal-stripe-connect {
max-width: 860px;
}
.fullscreen-modal-stripe-connected {
max-width: 440px;
}
.fullscreen-modal-stripe-connect .gh-main-section {
margin: 0 0 -32px;
}
.fullscreen-modal-stripe-connected .gh-main-section {
margin-bottom: -20px;
}
.gh-members-stripe-info-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.gh-members-stripe-info-header h4 {
font-weight: 600;
margin: 0;
padding: 0;
color: #555ABF;
}
.gh-members-stripe-info {
border-radius: 0.9rem;
background: color-mod(#555ABF a(12%));
padding: 12px;
width: 380px;
color: #555ABF;
}
.gh-members-stripe-badge {
width: 180px;
}
.gh-members-stripe-link,
.gh-members-stripe-link:hover {
color: #555ABF;
text-decoration: underline;
}
.gh-members-connectbutton-container {
max-width: 380px;
}
.gh-members-connectbutton-container .for-switch {
line-height: 1em;
}
.gh-members-connectbutton-container .for-switch label {
width: 36px !important
}
.gh-members-connectbutton-container .for-switch input:checked + .input-toggle-component {
background: #F1946A;
}
.gh-members-connect-testmodeon {
color: #F1946A;
}
.gh-members-stripe-connect-token {
background: var(--whitegrey-l2);
min-height: unset;
height: 80px;
max-width: 380px;
font-family: var(--font-family-mono);
font-size: 1.3rem;
resize: none;
}
.gh-members-connect-testmodelabel {
display: inline-block;
background: #f8e5b9;
color: #983705;
font-size: 1.2rem;
font-weight: 500;
line-height: 1em;
border-radius: 999px;
padding: 4px 8px;
}
.gh-members-connect-savecontainer {
height: 0px;
overflow-y: hidden;
transition: all 0.2s ease-in-out;
opacity: 0;
margin-top: 16px;
margin-bottom: 0;
}
.gh-members-connect-savecontainer.expanded {
margin-bottom: 20px;
}
.gh-members-connect-savecontainer.expanded {
height: 36px;
opacity: 1.0;
}
.gh-stripe-connected-container {
display: flex;
flex-direction: column;
align-items: center;
}
.gh-stripe-connected-container .check-circle {
width: 60px;
height: 60px;
color: var(--green);
margin-top: 20px;
}
.gh-stripe-connected-container .check-circle path {
stroke-width: 1px;
}
.gh-stripe-connected-container h1 {
font-size: 2.1rem;
font-weight: 600;
letter-spacing: -.1px;
margin: 20px 0 4px;
}
.gh-stripe-connected-info {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-bottom: 32px;
}
.gh-stripe-connected-info p {
margin-bottom: 8px;
}
.gh-btn-stripe-disconnect {
align-self: flex-start;
margin-bottom: -34px;
}
.gh-stripe-error-hasactivesub {
margin: 24px 24px -8px;
color: var(--red);
}
@media (max-width: 500px) {
.gh-members-stripe-info-header {
flex-direction: column;
align-items: stretch;
}
.gh-members-stripe-info-header h4 {
order: 2;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--whitegrey);
}
.gh-members-stripe-badge {
order: 1;
/* margin: -10px 0 0 -10px; */
}
.gh-members-stripe-info {
width: 100%;
}
}

View File

@ -56,8 +56,10 @@
<div class="gh-setting-members-tierscontainer">
<div class="gh-settings-members-tiersheader">
<h4 class="gh-main-section-header small bn">Membership tiers</h4>
<button type="button" class="gh-btn gh-btn-outline gh-btn-stripe-status">
<span>Stripe not connected</span>
<button type="button" class="gh-btn gh-btn-outline gh-btn-stripe-status {{if this.settings.stripeConnectAccountId "connected" ""}}" {{action (toggle "showStripeConnect" this)}}>
<span>
{{if this.settings.stripeConnectAccountId "Connected to Stripe" "Stripe not connected"}}
</span>
</button>
</div>
<section class="gh-expandable">
@ -100,7 +102,14 @@
<h4 class="gh-expandable-title">Premium</h4>
<p class="gh-expandable-description">Customise prices and premium signup options</p>
</div>
<button type="button" class="gh-btn" {{on "click" (toggle "paidOpen" this)}} data-test-toggle-pub-info><span>{{if this.paidOpen "Close" "Expand"}}</span></button>
{{#if this.settings.stripeConnectAccountId}}
<button type="button" class="gh-btn" {{action (toggle "paidOpen" this)}} data-test-toggle-pub-info><span>{{if this.paidOpen "Close" "Expand"}}</span></button>
{{else}}
<button type="button" class="stripe-connect" {{action (toggle "showStripeConnect" this)}}>
<span>Connect with Stripe</span>
</button>
{{/if}}
</div>
<div class="gh-expandable-content">
{{#liquid-if this.paidOpen}}
@ -207,4 +216,11 @@
@modifier="action wide"
/>
{{/if}}
{{#if this.showStripeConnect}}
<GhFullscreenModal
@modal="stripe-connect"
@close={{action "closeStripeConnect"}}
@modifier="action wide stripe-connect {{if this.settings.stripeConnectAccountId "stripe-connected" ""}}" />
{{/if}}
</section>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}</style></defs><title>check-circle</title><path class="a" d="M6.75,9l3.294,4.611a1.526,1.526,0,0,0,2.414.09L23.25.749"/><path class="a" d="M16.783,3.824A10.487,10.487,0,1,0,20.8,8.377"/></svg>

After

Width:  |  Height:  |  Size: 361 B