mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 22:43:30 +03:00
Removed all launch-wizard related code
No issue - With the onboarding flow redesign, the launch wizard can no longer be accessed and is therefore deleted.
This commit is contained in:
parent
0b8b530efb
commit
594e2ccb08
@ -1,123 +0,0 @@
|
|||||||
<div class="gh-launch-wizard-settings-container">
|
|
||||||
<div class="gh-stack overflow-y-auto flex-grow-1">
|
|
||||||
<div class="gh-stack-item gh-setting-first">
|
|
||||||
<div class="gh-members-stripe-info gh-launch-wizard-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. Ghost collects <strong>no fees</strong> on any payments! If you don’t have a Stripe account yet, you can <a href="https://stripe.com" target="_blank" rel="noopener noreferrer" class="gh-members-stripe-link">sign up here</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{#if this.config.stripeDirect}}
|
|
||||||
<div class="gh-stack-item gh-setting flex-column">
|
|
||||||
<div class="mb4">
|
|
||||||
<label for="stripe-publishable-key" class="gh-setting-title">Stripe Publishable key</label>
|
|
||||||
<GhTextInput
|
|
||||||
@id="stripe-publishable-key"
|
|
||||||
@type="password"
|
|
||||||
@value={{readonly this.settings.stripePublishableKey}}
|
|
||||||
class="mt1 password"
|
|
||||||
{{on "input" this.setStripeDirectPublicKey}}
|
|
||||||
/>
|
|
||||||
{{#if this.stripePublishableKeyError}}<p class="mb0 mt2 f8 red">{{this.stripePublishableKeyError}}</p>{{/if}}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="stripe-secret-key" class="gh-setting-title">Stripe Secret key</label>
|
|
||||||
<GhTextInput
|
|
||||||
@id="stripe-secret-key"
|
|
||||||
@type="password"
|
|
||||||
@value={{readonly this.settings.stripeSecretKey}}
|
|
||||||
class="mt1 password"
|
|
||||||
{{on "input" this.setStripeDirectSecretKey}}
|
|
||||||
/>
|
|
||||||
{{#if this.stripeSecretKeyError}}<p class="mb0 mt2 f8 red">{{this.stripeSecretKeyError}}</p>{{/if}}
|
|
||||||
<a href="https://dashboard.stripe.com/account/apikeys" target="_blank" rel="noopener noreferrer" class="mt1 fw4 f8">
|
|
||||||
Find your Stripe API keys here »
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gh-setting-desc"><a href="javascript:void(0)" {{on "click" @skipStep}}>Skip</a> if you don't want to offer paid subscriptions.</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="gh-stack-item gh-setting flex-wrap">
|
|
||||||
{{!-- Stripe already configured --}}
|
|
||||||
{{#if this.settings.stripeConnectAccountId}}
|
|
||||||
<div>
|
|
||||||
<h4 class="gh-setting-title">Already connected to Stripe</h4>
|
|
||||||
<p class="gh-setting-desc mt2">
|
|
||||||
Connected to <a href="https://dashboard.stripe.com/{{this.settings.stripeConnectAccountId}}" target="_blank" rel="noopener noreferrer">{{this.settings.stripeConnectDisplayName}}</a>
|
|
||||||
|
|
||||||
{{#unless this.settings.stripeConnectLivemode}}
|
|
||||||
<span class="gh-members-connect-testmodelabel">Test mode</span>
|
|
||||||
{{/unless}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{{#if this.hasActiveStripeSubscriptions}}
|
|
||||||
<p class="red ma0 pa0 f8 nudge-bottom--2">
|
|
||||||
Cannot disconnect while there are members with active Stripe subscriptions.
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<div class="gh-setting-action">
|
|
||||||
<button type="button" class="gh-btn" {{on "click" (perform this.openDisconnectStripeConnectModalTask)}}><span>Disconnect</span></button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{!-- Stripe not yet configured --}}
|
|
||||||
{{else}}
|
|
||||||
<div class="w-100">
|
|
||||||
<div class="gh-setting-title">Generate secure key</div>
|
|
||||||
<div class="flex items-center mb4 gh-members-connectbutton-container justify-between mt2">
|
|
||||||
<a href="{{this.stripeConnectAuthUrl}}" class="stripe-connect" target="_blank" rel="noopener noreferrer"><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"}}">Test mode</span>
|
|
||||||
<div class="for-switch small">
|
|
||||||
<label class="switch" for="stripe-connect-test-mode" {{on "click" this.toggleStripeConnectTestMode}}>
|
|
||||||
<input type="checkbox" class="gh-input" checked={{this.stripeConnectTestMode}} {{on "click" this.toggleStripeConnectTestMode}} data-test-checkbox="stripe-connect-test-mode">
|
|
||||||
<span class="input-toggle-component mt1"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gh-setting-action">
|
|
||||||
<GhTextarea
|
|
||||||
class="gh-launch-wizard-stripe-connect-token"
|
|
||||||
placeholder="Paste your secure key here"
|
|
||||||
{{on "input" this.setStripeConnectIntegrationToken}}
|
|
||||||
/>
|
|
||||||
{{#if this.stripeConnectError}}<p class="mb0 mt2 f8 red">{{this.stripeConnectError}}</p>{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="gh-setting-desc skip-step"><a href="javascript:void(0)" {{on "click" @skipStep}}>Skip</a> if you don't want to offer paid subscriptions.</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="gh-launch-wizard-nav-buttons">
|
|
||||||
<button type="button" class="gh-btn gh-btn-outline gh-btn-icon-dark gh-btn-large w-30" {{on "click" @backStep}}><span>{{svg-jar "arrow-left-tail"}}</span></button>
|
|
||||||
|
|
||||||
<GhTaskButton
|
|
||||||
@task={{this.saveAndContinueTask}}
|
|
||||||
@runningText="Saving"
|
|
||||||
@class="w-70 ml4 right gh-btn gh-btn-black gh-btn-large gh-btn-icon-right"
|
|
||||||
data-test-button="wizard-next"
|
|
||||||
>
|
|
||||||
{{#if this.saveAndContinueTask.isRunning}}
|
|
||||||
<span>Saving...</span>
|
|
||||||
{{else}}
|
|
||||||
<span>{{if this.settings.stripeConnectAccountId "Continue" "Save and continue"}}{{svg-jar "arrow-right-tail"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</GhTaskButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.showDisconnectStripeConnectModal}}
|
|
||||||
<GhFullscreenModal
|
|
||||||
@modal="disconnect-stripe"
|
|
||||||
@model={{hash
|
|
||||||
stripeConnectAccountName=this.settings.stripeConnectDisplayName
|
|
||||||
}}
|
|
||||||
@confirm={{perform this.disconnectStripeConnectIntegrationTask}}
|
|
||||||
@close={{this.closeDisconnectStripeModal}}
|
|
||||||
@modifier="action wide" />
|
|
||||||
{{/if}}
|
|
@ -1,203 +0,0 @@
|
|||||||
import Component from '@glimmer/component';
|
|
||||||
import {action} from '@ember/object';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
import {task, timeout} from 'ember-concurrency';
|
|
||||||
import {tracked} from '@glimmer/tracking';
|
|
||||||
|
|
||||||
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
|
|
||||||
const RETRY_PRODUCT_SAVE_MAX_POLL = 15 * RETRY_PRODUCT_SAVE_POLL_LENGTH;
|
|
||||||
|
|
||||||
export default class GhLaunchWizardConnectStripeComponent extends Component {
|
|
||||||
@service ajax;
|
|
||||||
@service config;
|
|
||||||
@service ghostPaths;
|
|
||||||
@service settings;
|
|
||||||
@service store;
|
|
||||||
|
|
||||||
@tracked hasActiveStripeSubscriptions = false;
|
|
||||||
@tracked showDisconnectStripeConnectModal = false;
|
|
||||||
@tracked stripeConnectTestMode = false;
|
|
||||||
@tracked stripeConnectError = null;
|
|
||||||
@tracked stripePublishableKeyError = null;
|
|
||||||
@tracked stripeSecretKeyError = null;
|
|
||||||
|
|
||||||
get stripeConnectAuthUrl() {
|
|
||||||
const mode = this.stripeConnectTestMode ? 'test' : 'live';
|
|
||||||
return `${this.ghostPaths.url.api('members/stripe_connect')}?mode=${mode}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.args.updatePreview('');
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy?.(...arguments);
|
|
||||||
// clear any unsaved settings changes when going back/forward/closing
|
|
||||||
this.settings.rollbackAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setStripeDirectPublicKey(event) {
|
|
||||||
this.settings.set('stripeProductName', this.settings.get('title'));
|
|
||||||
this.settings.set('stripePublishableKey', event.target.value);
|
|
||||||
this.stripePublishableKeyError = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setStripeDirectSecretKey(event) {
|
|
||||||
this.settings.set('stripeProductName', this.settings.get('title'));
|
|
||||||
this.settings.set('stripeSecretKey', event.target.value);
|
|
||||||
this.stripeSecretKeyError = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleStripeConnectTestMode() {
|
|
||||||
this.stripeConnectTestMode = !this.stripeConnectTestMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setStripeConnectIntegrationToken(event) {
|
|
||||||
this.settings.set('stripeProductName', this.settings.get('title'));
|
|
||||||
this.settings.set('stripeConnectIntegrationToken', event.target.value);
|
|
||||||
this.stripeConnectError = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateDiscount(monthly, yearly) {
|
|
||||||
if (isNaN(monthly) || isNaN(yearly)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return monthly ? 100 - Math.floor((yearly / 12 * 100) / monthly) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getActivePrice(prices, interval, amount, currency) {
|
|
||||||
return prices.find((price) => {
|
|
||||||
return (
|
|
||||||
price.active && price.amount === amount && price.type === 'recurring' &&
|
|
||||||
price.interval === interval && price.currency.toLowerCase() === currency.toLowerCase()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@task({drop: true})
|
|
||||||
*saveTier() {
|
|
||||||
let pollTimeout = 0;
|
|
||||||
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
|
|
||||||
yield timeout(RETRY_PRODUCT_SAVE_POLL_LENGTH);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const updatedTier = yield this.tier.save();
|
|
||||||
|
|
||||||
yield this.settings.save();
|
|
||||||
|
|
||||||
return updatedTier;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.payload?.errors && error.payload.errors[0].code === 'STRIPE_NOT_CONFIGURED') {
|
|
||||||
pollTimeout += RETRY_PRODUCT_SAVE_POLL_LENGTH;
|
|
||||||
// no-op: will try saving again as stripe is not ready
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.tier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@task({drop: true})
|
|
||||||
*openDisconnectStripeConnectModalTask() {
|
|
||||||
this.hasActiveStripeSubscriptions = false;
|
|
||||||
|
|
||||||
const url = this.ghostPaths.url.api('/members/') + '?filter=status:paid&limit=0';
|
|
||||||
const response = yield this.ajax.request(url);
|
|
||||||
|
|
||||||
if (response?.meta?.pagination?.total !== 0) {
|
|
||||||
this.hasActiveStripeSubscriptions = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showDisconnectStripeConnectModal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
closeDisconnectStripeModal() {
|
|
||||||
this.showDisconnectStripeConnectModal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*disconnectStripeConnectIntegrationTask() {
|
|
||||||
this.disconnectStripeError = false;
|
|
||||||
const url = this.ghostPaths.url.api('/settings/stripe/connect');
|
|
||||||
|
|
||||||
yield this.ajax.delete(url);
|
|
||||||
yield this.settings.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*saveAndContinueTask() {
|
|
||||||
if (this.config.get('stripeDirect')) {
|
|
||||||
if (!this.settings.get('stripePublishableKey')) {
|
|
||||||
this.stripePublishableKeyError = 'Enter your publishable key to continue';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.get('stripeSecretKey')) {
|
|
||||||
this.stripeSecretKeyError = 'Enter your secret key to continue';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stripePublishableKeyError || this.stripeSecretKeyError) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!this.settings.get('stripeConnectAccountId') && !this.settings.get('stripeConnectIntegrationToken')) {
|
|
||||||
this.stripeConnectError = 'Paste your secure key to continue';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.config.get('stripeDirect') && this.settings.get('stripeConnectAccountId')) {
|
|
||||||
this.args.nextStep();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
yield this.settings.save();
|
|
||||||
|
|
||||||
const tiers = yield this.store.query('tier', {filter: 'type:paid', include: 'monthly_price,yearly_price'});
|
|
||||||
this.tier = tiers.firstObject;
|
|
||||||
|
|
||||||
if (this.tier) {
|
|
||||||
this.tier.set('currency', 'usd');
|
|
||||||
this.tier.set('monthlyPrice', 500);
|
|
||||||
this.tier.set('yearlyPrice', 5000);
|
|
||||||
yield this.saveTier.perform();
|
|
||||||
this.settings.set('portalPlans', ['free', 'monthly', 'yearly']);
|
|
||||||
yield this.settings.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pauseAndContinueTask.perform();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.payload?.errors && error.payload.errors[0].type === 'ValidationError') {
|
|
||||||
const [validationError] = error.payload.errors;
|
|
||||||
|
|
||||||
if (this.config.get('stripeDirect')) {
|
|
||||||
if (validationError.context.match(/stripe_publishable_key/)) {
|
|
||||||
this.stripePublishableKeyError = 'Invalid publishable key';
|
|
||||||
} else {
|
|
||||||
this.stripeSecretKeyError = 'Invalid secret key';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.stripeConnectError = 'Invalid secure key';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*pauseAndContinueTask() {
|
|
||||||
this.args.refreshPreview();
|
|
||||||
yield timeout(500);
|
|
||||||
this.args.nextStep();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<div class="gh-branding-settings">
|
|
||||||
<section class="gh-launch-wizard-settings-container">
|
|
||||||
<GhBrandSettingsForm
|
|
||||||
class="overflow-y-auto flex-grow-1"
|
|
||||||
@replacePreviewContents={{@replacePreviewContents}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="gh-launch-wizard-nav-buttons">
|
|
||||||
<GhTaskButton
|
|
||||||
@task={{this.saveAndContinueTask}}
|
|
||||||
@buttonText={{html-safe (concat "Save and continue " (svg-jar "arrow-right-tail"))}}
|
|
||||||
type="button"
|
|
||||||
class="gh-btn gh-btn-black gh-btn-icon-right gh-btn-large gh-launch-wizard-btn w-100"
|
|
||||||
data-test-button="wizard-next"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||||||
import Component from '@glimmer/component';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
import {task} from 'ember-concurrency';
|
|
||||||
|
|
||||||
export default class GhLaunchWizardCustomiseDesignComponent extends Component {
|
|
||||||
@service notifications;
|
|
||||||
@service settings;
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy?.(...arguments);
|
|
||||||
this.settings.rollbackAttributes();
|
|
||||||
this.settings.errors.remove('accentColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*saveAndContinueTask() {
|
|
||||||
try {
|
|
||||||
if (this.settings.errors && this.settings.errors.length !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
yield this.settings.save();
|
|
||||||
this.args.nextStep();
|
|
||||||
} catch (error) {
|
|
||||||
if (error) {
|
|
||||||
this.notifications.showAPIError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<div class="gh-launch-wizard-settings-container">
|
|
||||||
<div class="overflow-auto flex-grow-1">
|
|
||||||
<h4>All looks good?</h4>
|
|
||||||
<p>You are all set up to start creating content, grow an audience and make your first sale!</p>
|
|
||||||
<p>You can further customize your site in Settings.</p>
|
|
||||||
</div>
|
|
||||||
<div class="gh-launch-wizard-nav-buttons">
|
|
||||||
<button type="button" class="gh-btn gh-btn-outline gh-btn-icon-dark gh-btn-large w-30" {{on "click" @backStep}}><span>{{svg-jar "arrow-left-tail"}}</span></button>
|
|
||||||
<GhTaskButton
|
|
||||||
@task={{this.finaliseTask}}
|
|
||||||
@buttonText="Launch your site!"
|
|
||||||
@runningText="Launching..."
|
|
||||||
@class="w-70 ml4 gh-btn gh-btn-black gh-btn-large gh-btn-icon gh-launch-wizard-btn"
|
|
||||||
data-test-button="wizard-finish"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,47 +0,0 @@
|
|||||||
import Component from '@glimmer/component';
|
|
||||||
import {htmlSafe} from '@ember/template';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
import {task} from 'ember-concurrency';
|
|
||||||
|
|
||||||
export default class GhLaunchWizardFinaliseComponent extends Component {
|
|
||||||
@service feature;
|
|
||||||
@service notifications;
|
|
||||||
@service router;
|
|
||||||
@service settings;
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy?.(...arguments);
|
|
||||||
// clear any unsaved settings changes when going back/forward/closing
|
|
||||||
this.settings.rollbackAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveTier() {
|
|
||||||
const data = this.args.getData();
|
|
||||||
this.tier = data?.tier;
|
|
||||||
if (this.tier) {
|
|
||||||
const monthlyAmount = Math.round(data.monthlyAmount * 100);
|
|
||||||
const yearlyAmount = Math.round(data.yearlyAmount * 100);
|
|
||||||
const currency = data.currency;
|
|
||||||
this.tier.set('monthlyPrice', monthlyAmount);
|
|
||||||
this.tier.set('yearlyPrice', yearlyAmount);
|
|
||||||
this.tier.set('currency', currency);
|
|
||||||
const savedTier = await this.tier.save();
|
|
||||||
return savedTier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*finaliseTask() {
|
|
||||||
const data = this.args.getData();
|
|
||||||
if (data?.tier) {
|
|
||||||
yield this.saveTier();
|
|
||||||
this.settings.set('editorIsLaunchComplete', true);
|
|
||||||
yield this.settings.save();
|
|
||||||
}
|
|
||||||
this.router.transitionTo('dashboard');
|
|
||||||
this.notifications.showNotification(
|
|
||||||
'Launch complete!',
|
|
||||||
{type: 'success', actions: htmlSafe('<a href="#/posts">Start creating content</a>')}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
<div class="gh-launch-wizard-settings-container" {{did-insert this.setup}}>
|
|
||||||
{{#if this.isConnectDisallowed}}
|
|
||||||
<div class="gh-stack overflow-y-auto flex-grow-1">
|
|
||||||
<div class="gh-setting-nossl-container">
|
|
||||||
<span class="red">{{svg-jar "shield-lock"}}</span>
|
|
||||||
<h4>Your site is not secured</h4>
|
|
||||||
<p>Paid memberships through Ghost can only be run on sites secured by SSL (HTTPS vs. HTTP). More information on adding a free SSL Certificate to your Ghost site can be <a href="https://ghost.org/integrations/lets-encrypt/" target="_blank" rel="noopener noreferrer">found here</a>.</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 mt6">
|
|
||||||
<div class="gh-setting-title">Generate secure key</div>
|
|
||||||
<div class="flex items-center mb4 gh-members-connectbutton-container justify-between mt2">
|
|
||||||
<div class="stripe-connect disabled"><span>Connect with Stripe</span></div>
|
|
||||||
<div class="ml2 flex items-center flex-nowrap">
|
|
||||||
<span class="mr2 f8 midgrey nowrap">Test mode</span>
|
|
||||||
<div class="for-switch small disabled">
|
|
||||||
<label class="switch" for="stripe-connect-test-mode">
|
|
||||||
<input type="checkbox" class="gh-input" disabled="disabled">
|
|
||||||
<span class="input-toggle-component mt1"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gh-setting-action">
|
|
||||||
<GhTextarea
|
|
||||||
class="gh-launch-wizard-stripe-connect-token"
|
|
||||||
placeholder="Paste your secure key here"
|
|
||||||
disabled="disabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="gh-stack overflow-y-auto flex-grow-1">
|
|
||||||
<div class="gh-stack-item flex-column">
|
|
||||||
<div class="w-100">
|
|
||||||
<GhFormGroup @class="for-select">
|
|
||||||
<div class="gh-setting-title" for="currency">Plan currency</div>
|
|
||||||
<span class="gh-select mt2">
|
|
||||||
<OneWaySelect
|
|
||||||
@disabled={{this.disabled}}
|
|
||||||
@value={{this.selectedCurrency}}
|
|
||||||
id="currency"
|
|
||||||
name="currency"
|
|
||||||
@options={{readonly this.allCurrencies}}
|
|
||||||
@optionValuePath="value"
|
|
||||||
@optionLabelPath="label"
|
|
||||||
@update={{this.setStripePlansCurrency}}
|
|
||||||
/>
|
|
||||||
{{svg-jar "arrow-down-small"}}
|
|
||||||
</span>
|
|
||||||
</GhFormGroup>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 flex flex-column flex-row-ns">
|
|
||||||
<div class="w-100 w-50-ns mr3-ns">
|
|
||||||
<GhFormGroup>
|
|
||||||
<div class="gh-setting-title">Monthly price</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-center mt2 gh-input-group gh-labs-price-label">
|
|
||||||
<GhTextInput
|
|
||||||
@disabled={{this.disabled}}
|
|
||||||
@value={{readonly this.stripeMonthlyAmount}}
|
|
||||||
@type="number"
|
|
||||||
@input={{action (mut this.stripeMonthlyAmount) value="target.value"}}
|
|
||||||
{{on "blur" this.validateStripePlans}}
|
|
||||||
/>
|
|
||||||
<span class="gh-input-append"><span class="ttu">{{this.currency}}</span>/month</span>
|
|
||||||
</div>
|
|
||||||
</GhFormGroup>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 w-50-ns ml2-ns">
|
|
||||||
<GhFormGroup>
|
|
||||||
<div class="gh-setting-title">Yearly price</div>
|
|
||||||
<div class="flex items-center justify-center mt2 gh-input-group gh-labs-price-label">
|
|
||||||
<GhTextInput
|
|
||||||
@disabled={{this.disabled}}
|
|
||||||
@value={{readonly this.stripeYearlyAmount}}
|
|
||||||
@type="number"
|
|
||||||
@input={{action (mut this.stripeYearlyAmount) value="target.value"}}
|
|
||||||
{{on "blur" this.validateStripePlans}}
|
|
||||||
/>
|
|
||||||
<span class="gh-input-append"><span class="ttu">{{this.currency}}</span>/year</span>
|
|
||||||
</div>
|
|
||||||
</GhFormGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 w-50-l flex flex-column flex-row-ns">
|
|
||||||
{{#if this.stripePlanError}}
|
|
||||||
<p class="response w-100 red"> {{this.stripePlanError}} </p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gh-stack-item gh-setting flex-column">
|
|
||||||
<div class="gh-setting-title">Plans available at signup</div>
|
|
||||||
<div class="form-group mt2 mb0 for-checkbox">
|
|
||||||
<label
|
|
||||||
class="checkbox"
|
|
||||||
for="free-plan"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={{this.isFreeChecked}}
|
|
||||||
id="free-plan"
|
|
||||||
name="free-plan"
|
|
||||||
disabled={{this.isFreeDisabled}}
|
|
||||||
class="gh-input post-settings-featured"
|
|
||||||
{{on "click" this.toggleFreePlan}}
|
|
||||||
data-test-checkbox="featured"
|
|
||||||
>
|
|
||||||
<span class="input-toggle-component"></span>
|
|
||||||
<p>Free</p>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb0 for-checkbox">
|
|
||||||
<label
|
|
||||||
class="checkbox"
|
|
||||||
for="monthly-plan"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="monthly-plan"
|
|
||||||
name="monthly-plan"
|
|
||||||
checked={{this.isMonthlyChecked}}
|
|
||||||
disabled={{not this.membersUtils.isStripeEnabled}}
|
|
||||||
class="gh-input post-settings-featured"
|
|
||||||
{{on "click" this.toggleMonthlyPlan}}
|
|
||||||
data-test-checkbox="featured"
|
|
||||||
>
|
|
||||||
<span class="input-toggle-component"></span>
|
|
||||||
<p>Monthly</p>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb0 for-checkbox">
|
|
||||||
<label
|
|
||||||
class="checkbox"
|
|
||||||
for="yearly-plan"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="yearly-plan"
|
|
||||||
name="yearly-plan"
|
|
||||||
checked={{this.isYearlyChecked}}
|
|
||||||
disabled={{not this.membersUtils.isStripeEnabled}}
|
|
||||||
class="gh-input post-settings-featured"
|
|
||||||
{{on "click" this.toggleYearlyPlan}}
|
|
||||||
data-test-checkbox="featured"
|
|
||||||
>
|
|
||||||
<span class="input-toggle-component"></span>
|
|
||||||
<p>Yearly</p>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="gh-launch-wizard-nav-buttons">
|
|
||||||
<button type="button" class="gh-btn gh-btn-outline gh-btn-icon-dark gh-btn-large w-30" {{on "click" this.backStep}}><span>{{svg-jar "arrow-left-tail"}}</span></button>
|
|
||||||
|
|
||||||
{{!-- TODO: reset "failed" state automatically --}}
|
|
||||||
<GhTaskButton
|
|
||||||
@task={{this.saveAndContinue}}
|
|
||||||
@runningText="Saving"
|
|
||||||
@class="w-70 ml4 right gh-btn gh-btn-black gh-btn-large gh-btn-icon-right"
|
|
||||||
data-test-button="wizard-next"
|
|
||||||
>
|
|
||||||
<span>{{if this.isHidden "Continue" "Save and continue"}}{{svg-jar "arrow-right-tail"}}</span>
|
|
||||||
</GhTaskButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,209 +0,0 @@
|
|||||||
import Component from '@glimmer/component';
|
|
||||||
import envConfig from 'ghost-admin/config/environment';
|
|
||||||
import {action} from '@ember/object';
|
|
||||||
import {currencies, getCurrencyOptions, getSymbol} from 'ghost-admin/utils/currency';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
import {task} from 'ember-concurrency';
|
|
||||||
import {tracked} from '@glimmer/tracking';
|
|
||||||
|
|
||||||
const CURRENCIES = currencies.map((currency) => {
|
|
||||||
return {
|
|
||||||
value: currency.isoCode.toLowerCase(),
|
|
||||||
label: `${currency.isoCode} - ${currency.name}`,
|
|
||||||
isoCode: currency.isoCode
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class GhLaunchWizardSetPricingComponent extends Component {
|
|
||||||
@service config;
|
|
||||||
@service membersUtils;
|
|
||||||
@service settings;
|
|
||||||
@service store;
|
|
||||||
|
|
||||||
currencies = CURRENCIES;
|
|
||||||
|
|
||||||
@tracked stripeMonthlyAmount = 5;
|
|
||||||
@tracked stripeYearlyAmount = 50;
|
|
||||||
@tracked currency = 'usd';
|
|
||||||
@tracked isFreeChecked = true;
|
|
||||||
@tracked isMonthlyChecked = true;
|
|
||||||
@tracked isYearlyChecked = true;
|
|
||||||
@tracked stripePlanError = '';
|
|
||||||
@tracked tier;
|
|
||||||
@tracked loadingTier = false;
|
|
||||||
|
|
||||||
get selectedCurrency() {
|
|
||||||
return this.currencies.findBy('value', this.currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
get allCurrencies() {
|
|
||||||
return getCurrencyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
get isConnectDisallowed() {
|
|
||||||
const siteUrl = this.config.get('blogUrl');
|
|
||||||
|
|
||||||
return envConfig.environment !== 'development' && !/^https:/.test(siteUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isFreeDisabled() {
|
|
||||||
return this.settings.get('membersSignupAccess') !== 'all';
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroy() {
|
|
||||||
super.willDestroy?.(...arguments);
|
|
||||||
// clear any unsaved settings changes when going back/forward/closing
|
|
||||||
this.args.updatePreview('');
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setup() {
|
|
||||||
this.fetchDefaultTier.perform();
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
backStep() {
|
|
||||||
const tier = this.tier;
|
|
||||||
const data = this.args.getData() || {};
|
|
||||||
this.args.storeData({
|
|
||||||
...data,
|
|
||||||
tier,
|
|
||||||
isFreeChecked: this.isFreeChecked,
|
|
||||||
isMonthlyChecked: this.isMonthlyChecked,
|
|
||||||
isYearlyChecked: this.isYearlyChecked,
|
|
||||||
monthlyAmount: this.stripeMonthlyAmount,
|
|
||||||
yearlyAmount: this.stripeYearlyAmount,
|
|
||||||
currency: this.currency
|
|
||||||
});
|
|
||||||
this.args.backStep();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setStripePlansCurrency(event) {
|
|
||||||
const newCurrency = event.value;
|
|
||||||
this.currency = newCurrency;
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleFreePlan(event) {
|
|
||||||
this.isFreeChecked = event.target.checked;
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleMonthlyPlan(event) {
|
|
||||||
this.isMonthlyChecked = event.target.checked;
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleYearlyPlan(event) {
|
|
||||||
this.isYearlyChecked = event.target.checked;
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
validateStripePlans() {
|
|
||||||
this.stripePlanError = undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const yearlyAmount = this.stripeYearlyAmount;
|
|
||||||
const monthlyAmount = this.stripeMonthlyAmount;
|
|
||||||
const symbol = getSymbol(this.currency);
|
|
||||||
if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) {
|
|
||||||
throw new TypeError(`Subscription amount must be at least ${symbol}1.00`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
} catch (err) {
|
|
||||||
this.stripePlanError = err.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@task
|
|
||||||
*saveAndContinue() {
|
|
||||||
if (this.isConnectDisallowed) {
|
|
||||||
this.args.nextStep();
|
|
||||||
} else {
|
|
||||||
yield this.validateStripePlans();
|
|
||||||
|
|
||||||
if (this.stripePlanError) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const tier = this.tier;
|
|
||||||
const data = this.args.getData() || {};
|
|
||||||
this.args.storeData({
|
|
||||||
...data,
|
|
||||||
tier,
|
|
||||||
isFreeChecked: this.isFreeChecked,
|
|
||||||
isMonthlyChecked: this.isMonthlyChecked,
|
|
||||||
isYearlyChecked: this.isYearlyChecked,
|
|
||||||
monthlyAmount: this.stripeMonthlyAmount,
|
|
||||||
yearlyAmount: this.stripeYearlyAmount,
|
|
||||||
currency: this.currency
|
|
||||||
});
|
|
||||||
this.args.nextStep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@task({drop: true})
|
|
||||||
*fetchDefaultTier() {
|
|
||||||
const storedData = this.args.getData();
|
|
||||||
if (storedData?.tier) {
|
|
||||||
this.tier = storedData.tier;
|
|
||||||
|
|
||||||
if (storedData.isMonthlyChecked !== undefined) {
|
|
||||||
this.isMonthlyChecked = storedData.isMonthlyChecked;
|
|
||||||
}
|
|
||||||
if (storedData.isYearlyChecked !== undefined) {
|
|
||||||
this.isYearlyChecked = storedData.isYearlyChecked;
|
|
||||||
}
|
|
||||||
if (storedData.isFreeChecked !== undefined) {
|
|
||||||
this.isFreeChecked = storedData.isFreeChecked;
|
|
||||||
}
|
|
||||||
if (storedData.currency !== undefined) {
|
|
||||||
this.currency = storedData.currency;
|
|
||||||
}
|
|
||||||
this.stripeMonthlyAmount = storedData.monthlyAmount;
|
|
||||||
this.stripeYearlyAmount = storedData.yearlyAmount;
|
|
||||||
} else {
|
|
||||||
const tiers = yield this.store.query('tier', {filter: 'type:paid', include: 'monthly_price,yearly_price'});
|
|
||||||
this.tier = tiers.firstObject;
|
|
||||||
|
|
||||||
let portalPlans = this.settings.get('portalPlans') || [];
|
|
||||||
|
|
||||||
this.isMonthlyChecked = portalPlans.includes('monthly');
|
|
||||||
this.isYearlyChecked = portalPlans.includes('yearly');
|
|
||||||
this.isFreeChecked = portalPlans.includes('free');
|
|
||||||
|
|
||||||
const monthlyPrice = this.tier.get('monthlyPrice');
|
|
||||||
const yearlyPrice = this.tier.get('yearlyPrice');
|
|
||||||
if (monthlyPrice && monthlyPrice.amount) {
|
|
||||||
this.stripeMonthlyAmount = (monthlyPrice.amount / 100);
|
|
||||||
this.currency = monthlyPrice.currency;
|
|
||||||
}
|
|
||||||
if (yearlyPrice && yearlyPrice.amount) {
|
|
||||||
this.stripeYearlyAmount = (yearlyPrice.amount / 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updatePreviewUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePreviewUrl() {
|
|
||||||
const options = {
|
|
||||||
disableBackground: true,
|
|
||||||
currency: this.selectedCurrency.value,
|
|
||||||
monthlyPrice: Math.round(this.stripeMonthlyAmount * 100),
|
|
||||||
yearlyPrice: Math.round(this.stripeYearlyAmount * 100),
|
|
||||||
isMonthlyChecked: this.isMonthlyChecked,
|
|
||||||
isYearlyChecked: this.isYearlyChecked,
|
|
||||||
isFreeChecked: this.isFreeChecked,
|
|
||||||
portalPlans: null
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = this.membersUtils.getPortalPreviewUrl(options);
|
|
||||||
this.args.updatePreview(url);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
import Controller from '@ember/controller';
|
|
||||||
import envConfig from 'ghost-admin/config/environment';
|
|
||||||
import {action} from '@ember/object';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
import {tracked} from '@glimmer/tracking';
|
|
||||||
|
|
||||||
const DEFAULT_STEPS = {
|
|
||||||
'customise-design': {
|
|
||||||
title: 'Customise your site',
|
|
||||||
position: 'Step 1',
|
|
||||||
next: 'connect-stripe'
|
|
||||||
},
|
|
||||||
'connect-stripe': {
|
|
||||||
title: 'Connect to Stripe',
|
|
||||||
position: 'Step 2',
|
|
||||||
next: 'set-pricing',
|
|
||||||
back: 'customise-design',
|
|
||||||
skip: 'finalise'
|
|
||||||
},
|
|
||||||
'set-pricing': {
|
|
||||||
title: 'Set up subscriptions',
|
|
||||||
position: 'Step 3',
|
|
||||||
next: 'finalise',
|
|
||||||
back: 'connect-stripe'
|
|
||||||
},
|
|
||||||
finalise: {
|
|
||||||
title: 'Launch your site',
|
|
||||||
position: 'Final step',
|
|
||||||
back: 'set-pricing'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export default class LaunchController extends Controller {
|
|
||||||
@service config;
|
|
||||||
@service router;
|
|
||||||
@service settings;
|
|
||||||
|
|
||||||
queryParams = ['step'];
|
|
||||||
|
|
||||||
@tracked previewGuid = (new Date()).valueOf();
|
|
||||||
@tracked previewSrc = '';
|
|
||||||
@tracked step = 'customise-design';
|
|
||||||
@tracked data = null;
|
|
||||||
|
|
||||||
steps = DEFAULT_STEPS;
|
|
||||||
|
|
||||||
skippedSteps = [];
|
|
||||||
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
const siteUrl = this.config.get('blogUrl');
|
|
||||||
|
|
||||||
if (envConfig.environment !== 'development' && !/^https:/.test(siteUrl)) {
|
|
||||||
this.steps = {
|
|
||||||
'customise-design': {
|
|
||||||
title: 'Customise your site',
|
|
||||||
position: 'Step 1',
|
|
||||||
next: 'set-pricing'
|
|
||||||
},
|
|
||||||
'set-pricing': {
|
|
||||||
title: 'Set up subscriptions',
|
|
||||||
position: 'Step 2',
|
|
||||||
next: 'finalise',
|
|
||||||
back: 'customise-design'
|
|
||||||
},
|
|
||||||
finalise: {
|
|
||||||
title: 'Launch your site',
|
|
||||||
position: 'Final step',
|
|
||||||
back: 'set-pricing'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.steps = DEFAULT_STEPS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentStep() {
|
|
||||||
return this.steps[this.step];
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
storeData(data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
getData() {
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
goToStep(step) {
|
|
||||||
if (step) {
|
|
||||||
this.step = step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
goNextStep() {
|
|
||||||
this.step = this.currentStep.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
goBackStep() {
|
|
||||||
let step = this.currentStep.back;
|
|
||||||
|
|
||||||
while (this.skippedSteps.includes(step)) {
|
|
||||||
this.skippedSteps = this.skippedSteps.filter(s => s !== step);
|
|
||||||
step = this.steps[step].back;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.step = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remember when a step is skipped so "back" works as expected
|
|
||||||
@action
|
|
||||||
skipStep() {
|
|
||||||
let step = this.currentStep.next;
|
|
||||||
let skipToStep = this.currentStep.skip;
|
|
||||||
|
|
||||||
while (step !== skipToStep) {
|
|
||||||
this.skippedSteps.push(step);
|
|
||||||
step = this.steps[step].next;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.step = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
registerPreviewIframe(element) {
|
|
||||||
this.previewIframe = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
refreshPreview() {
|
|
||||||
this.previewGuid = (new Date()).valueOf();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updatePreview(url) {
|
|
||||||
this.previewSrc = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
replacePreviewContents(html) {
|
|
||||||
if (this.previewIframe) {
|
|
||||||
this.previewIframe.contentWindow.document.open();
|
|
||||||
this.previewIframe.contentWindow.document.write(html);
|
|
||||||
this.previewIframe.contentWindow.document.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
close() {
|
|
||||||
this.router.transitionTo('dashboard');
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
reset() {
|
|
||||||
this.data = null;
|
|
||||||
this.step = 'customise-design';
|
|
||||||
this.skippedSteps = [];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
|
||||||
import {inject as service} from '@ember/service';
|
|
||||||
|
|
||||||
export default class LaunchRoute extends AuthenticatedRoute {
|
|
||||||
@service session;
|
|
||||||
|
|
||||||
beforeModel() {
|
|
||||||
super.beforeModel(...arguments);
|
|
||||||
if (!this.session.user.isOwnerOnly) {
|
|
||||||
return this.transitionTo('home');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
<div class="fullscreen-wizard-container" {{will-destroy this.reset}}>
|
|
||||||
<div class="pt7 pb5 pl12 pr12 flex justify-between items-center">
|
|
||||||
<div class="flex flex-column">
|
|
||||||
<div class="ttu gh-launch-wizard-step-indicator">{{this.currentStep.position}}</div>
|
|
||||||
<h2>{{this.currentStep.title}}</h2>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="close gh-btn gh-btn-outline" {{on "click" this.close}} data-test-button="close-wizard">
|
|
||||||
<span>Cancel</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gh-launch-wizard-content">
|
|
||||||
<div class="gh-launch-wizard-content-left">
|
|
||||||
{{component (concat "gh-launch-wizard/" this.step)
|
|
||||||
nextStep=this.goNextStep
|
|
||||||
backStep=this.goBackStep
|
|
||||||
skipStep=this.skipStep
|
|
||||||
refreshPreview=this.refreshPreview
|
|
||||||
updatePreview=this.updatePreview
|
|
||||||
replacePreviewContents=this.replacePreviewContents
|
|
||||||
storeData=this.storeData
|
|
||||||
getData=this.getData
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gh-launch-wizard-content-right">
|
|
||||||
<GhBrowserPreview class="gh-launch-wizard-preview-container" @icon={{this.settings.icon}} @title={{this.config.blogTitle}}>
|
|
||||||
<GhSiteIframe class="gh-launch-wizard-preview" @src={{this.previewSrc}} @guid={{this.previewGuid}} {{did-insert this.registerPreviewIframe}}></GhSiteIframe>
|
|
||||||
</GhBrowserPreview>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||||||
import {authenticateSession} from 'ember-simple-auth/test-support';
|
|
||||||
import {currentURL, visit} from '@ember/test-helpers';
|
|
||||||
import {describe, it} from 'mocha';
|
|
||||||
import {expect} from 'chai';
|
|
||||||
import {setupApplicationTest} from 'ember-mocha';
|
|
||||||
import {setupMirage} from 'ember-cli-mirage/test-support';
|
|
||||||
|
|
||||||
describe('Acceptance: Launch flow', function () {
|
|
||||||
const hooks = setupApplicationTest();
|
|
||||||
setupMirage(hooks);
|
|
||||||
|
|
||||||
it('is not accessible when logged out', async function () {
|
|
||||||
await visit('/launch');
|
|
||||||
expect(currentURL()).to.equal('/signin');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when logged in', function () {
|
|
||||||
beforeEach(async function () {
|
|
||||||
let role = this.server.create('role', {name: 'Owner'});
|
|
||||||
this.server.create('user', {roles: [role]});
|
|
||||||
|
|
||||||
return await authenticateSession();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can visit /launch', async function () {
|
|
||||||
await visit('/launch');
|
|
||||||
expect(currentURL()).to.equal('/launch');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user