Added new FirstPromoter integration (#1825)

no issue

Adds new FirstPromoter integration on the integrations page. FirstPromoter enables sites to launch their own members referral program, and integration allows Site admins to directly add their FirstPromoter tracking ID in the settings to enable FirstPromoter script on their site.
This commit is contained in:
Rishabh Garg 2021-01-15 20:01:40 +05:30 committed by GitHub
parent a1da989d21
commit 06d47f53e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 220 additions and 2 deletions

View File

@ -0,0 +1,70 @@
/* eslint-disable ghost/ember/alias-model-in-controller */
import Controller from '@ember/controller';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
export default Controller.extend({
notifications: service(),
settings: service(),
leaveSettingsTransition: null,
actions: {
update(value) {
this.settings.set('firstpromoter', value);
},
save() {
this.save.perform();
},
toggleLeaveSettingsModal(transition) {
let leaveTransition = this.leaveSettingsTransition;
if (!transition && this.showLeaveSettingsModal) {
this.set('leaveSettingsTransition', null);
this.set('showLeaveSettingsModal', false);
return;
}
if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
// we genuinely have unsaved data, show the modal
this.set('showLeaveSettingsModal', true);
}
},
leaveSettings() {
let transition = this.leaveSettingsTransition;
let settings = this.settings;
if (!transition) {
this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'});
return;
}
// roll back changes on settings model
settings.rollbackAttributes();
return transition.retry();
}
},
save: task(function* () {
try {
yield this.settings.validate();
return yield this.settings.save();
} catch (error) {
this.notifications.showAPIError(error);
throw error;
}
}).drop()
});

View File

@ -26,6 +26,8 @@ export default Model.extend(ValidationEngine, {
slack: attr('slack-settings'),
amp: attr('boolean'),
ampGtagId: attr('string'),
firstpromoter: attr('boolean'),
firstpromoterId: attr('string'),
unsplash: attr('unsplash-settings', {
defaultValue() {
return {isActive: true};

View File

@ -59,6 +59,7 @@ Router.map(function () {
});
this.route('settings.integrations.slack', {path: '/settings/integrations/slack'});
this.route('settings.integrations.amp', {path: '/settings/integrations/amp'});
this.route('settings.integrations.firstpromoter', {path: '/settings/integrations/firstpromoter'});
this.route('settings.integrations.unsplash', {path: '/settings/integrations/unsplash'});
this.route('settings.integrations.zapier', {path: '/settings/integrations/zapier'});

View File

@ -0,0 +1,39 @@
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings';
import {inject as service} from '@ember/service';
export default AuthenticatedRoute.extend(CurrentUserSettings, {
settings: service(),
beforeModel() {
this._super(...arguments);
return this.get('session.user')
.then(this.transitionAuthor())
.then(this.transitionEditor())
.then(this.settings.reload());
},
actions: {
save() {
this.controller.send('save');
},
willTransition(transition) {
let controller = this.controller;
let modelIsDirty = this.settings.get('hasDirtyAttributes');
if (modelIsDirty) {
transition.abort();
controller.send('toggleLeaveSettingsModal', transition);
return;
}
}
},
buildRouteInfoMetadata() {
return {
titleToken: 'FirstPromoter'
};
}
});

View File

@ -27,7 +27,7 @@ export default Service.extend(_ProxyMixin, ValidationEngine, {
_loadSettings() {
if (!this._loadingPromise) {
this._loadingPromise = this.store
.queryRecord('setting', {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views'})
.queryRecord('setting', {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter'})
.then((settings) => {
this._loadingPromise = null;
return settings;

View File

@ -899,6 +899,11 @@ p.theme-validation-details {
padding-right: 40px;
}
.gh-setting-firstpromoter-liquid {
padding-left: 40px;
padding-right: 40px;
}
.gh-setting-unsplash-checkbox {
margin-bottom: 0;
}

View File

@ -153,7 +153,29 @@
</article>
</LinkTo>
</div>
<div class="apps-grid-cell" data-test-app="firstpromoter">
<LinkTo @route="settings.integrations.firstpromoter" data-test-link="firstpromoter">
<article class="apps-card-app">
<div class="apps-card-left">
<figure class="apps-card-app-icon id-unsplash" style="background-image:url(assets/icons/firstpromoter.png); background-size:30px;"></figure>
<div class="apps-card-meta">
<h3 class="apps-card-app-title">FirstPromoter</h3>
<p class="apps-card-app-desc">Launch your member referral program</p>
</div>
</div>
<div class="gh-card-right">
<div class="apps-configured">
{{#if this.settings.firstpromoter}}
<span class="gh-badge" data-test-app-status>Active</span>
{{else}}
<span data-test-app-status>Configure</span>
{{/if}}
{{svg-jar "arrow-right"}}
</div>
</div>
</article>
</LinkTo>
</div>
</div>
</section>

View File

@ -0,0 +1,79 @@
<section class="gh-canvas">
<GhCanvasHeader class="gh-canvas-header">
<h2 class="gh-canvas-title" data-test-screen-title>
<LinkTo @route="settings.integrations" data-test-link="integrations-back">Integrations</LinkTo>
<span>{{svg-jar "arrow-right"}}</span>
FirstPromoter
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.save}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
</section>
</GhCanvasHeader>
{{#if this.showLeaveSettingsModal}}
<GhFullscreenModal @modal="leave-settings"
@confirm={{action "leaveSettings"}}
@close={{action "toggleLeaveSettingsModal"}}
@modifier="action wide" />
{{/if}}
<section class="view-container bt b--lightgrey-d1 pt5">
<section class="app-grid">
<div class="app-cell">
<div class="bg-white mr3 display flex items-center justify-center br-pill shadow-1 pa3 dark-no-shadow">
<img class="app-icon" src="assets/img/firstpromoter.png" />
</div>
</div>
<div class="app-cell">
<h3>FirstPromoter</h3>
<p>Launch your own member referral program</p>
</div>
</section>
<div class="gh-setting-header gh-first-header">FirstPromoter configuration</div>
<div class="flex flex-column br4 shadow-1 bg-grouped-table pa5 mt2">
<div class="gh-setting-first">
<div class="gh-setting-content">
<div class="gh-setting-title">Enable FirstPromoter</div>
<div class="gh-setting-desc">Enable <a href="https://firstpromoter.com/?fpr=ghost&fp_sid=admin" target="_blank">FirstPromoter</a> for tracking referrals</div>
</div>
<div class="gh-setting-action">
<div class="for-checkbox">
<label for="firstpromoter" class="checkbox">
<input
type="checkbox"
checked={{this.settings.firstpromoter}}
id="firstpromoter"
name="firstpromoter"
onclick={{action "update" value="target.checked"}}
data-test-firstpromoter-checkbox
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
{{#liquid-if this.settings.firstpromoter class="nl5 nr5"}}
<div class="gh-setting-last gh-setting-firstpromoter-liquid">
<div class="gh-setting-content gh-setting-content--no-action">
<div class="gh-setting-title">FirstPromoter Tracking ID</div>
<div class="gh-setting-desc"> Affiliate and referral tracking, find your ID here </div>
<div class="gh-setting-content-extended">
<GhFormGroup @errors={{this.settings.errors}} @hasValidated={{this.settings.hasValidated}} @property="firstpromoterId">
<GhTextInput
@placeholder="XXXXXXXX"
@name="firstpromoter_id"
@value={{this.settings.firstpromoterId}}
@keyEvents={{hash
Enter=(action "save")
}}
/>
<GhErrorMessage @errors={{this.settings.errors}} @property="firstpromoterId"/>
</GhFormGroup>
</div>
</div>
</div>
{{/liquid-if}}
</div>
</section>
</section>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB