Update offer details structure

- Added static UI to offer details page
- Deleted offer modal
This commit is contained in:
Peter Zimon 2021-10-04 15:00:41 +02:00
parent 6e6c89ba00
commit 96ec92a47b
12 changed files with 265 additions and 247 deletions

View File

@ -1,140 +0,0 @@
<section class="modal-content" {{did-insert this.setup}}>
<div class="gh-offer-modal-content">
<header class="modal-header">
<h1>New offer</h1>
</header>
<form>
<div class="modal-body">
<div class="gh-main-section columns-3">
<div class="gh-main-section-block no-margin span-2">
<h4 class="gh-main-section-header small bn">Basic</h4>
<div class="gh-main-section-content grey">
<GhFormGroup @errors={{this.errors}} @property="name">
<label for="name" class="fw6">Name</label>
<GhTextInput
@name="name"
@placeholder="Black Friday"
@id="name"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="name" />
<p>User internally</p>
</GhFormGroup>
</div>
<h4 class="gh-main-section-header small bn">Discount info</h4>
<div class="gh-main-section-content grey">
<GhFormGroup @errors={{this.errors}} @property="product-cadence">
<label for="product-cadence" class="fw6">Product cadence</label>
<span class="gh-select">
<OneWaySelect @value={{this.selectedVisibility}}
@options={{this.cadences}}
@optionValuePath="name"
@optionLabelPath="label"
@optionTargetPath="name"
@update={{this.updateVisibility}}
/>
{{svg-jar "arrow-down-small"}}
</span>
<GhErrorMessage @errors={{this.errors}} @property="product-cadence" />
</GhFormGroup>
<div class="form-col2">
<div class="gh-offer-type">
<div class="gh-radio {{if (eq this.selectedDiscountType "percentage") "active"}}" {{on "click" (fn this.setDiscountType "percentage")}}>
<div class="gh-radio-button"></div>
<div class="gh-radio-content">
<div class="gh-radio-label">Percentage discount</div>
</div>
</div>
<div class="gh-radio {{if (eq this.selectedDiscountType "fixed") "active"}}" {{on "click" (fn this.setDiscountType "fixed")}}>
<div class="gh-radio-button"></div>
<div class="gh-radio-content">
<div class="gh-radio-label">Fixed amount discount</div>
</div>
</div>
</div>
{{#if (eq this.selectedDiscountType "percentage")}}
<GhFormGroup @errors={{this.errors}} @property="amount">
<label for="amount" class="fw6">Percentage off</label>
<GhTextInput
@type="number"
@name="amount"
@placeholder=""
@id="amount"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="amount" />
</GhFormGroup>
{{else}}
<GhFormGroup @errors={{this.errors}} @property="amount">
<label for="amount" class="fw6">Amount off</label>
<GhTextInput
@name="amount"
@type="number"
@placeholder=""
@id="amount"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="amount" />
</GhFormGroup>
{{/if}}
</div>
</div>
<h4 class="gh-main-section-header small bn">Portal settings</h4>
<div class="gh-main-section-content grey">
<div class="form-col2">
<GhFormGroup @errors={{this.errors}} @property="display-title">
<label for="display-title" class="fw6">Display title</label>
<GhTextInput
@name="display-title"
@placeholder="Black Friday Special"
@id="display-title"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="display-title" />
</GhFormGroup>
<GhFormGroup @errors={{this.errors}} @property="code">
<label for="code" class="fw6">Offer code</label>
<GhTextInput
@name="code"
@placeholder="black-friday"
@id="code"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="code" />
</GhFormGroup>
</div>
<GhFormGroup @errors={{this.errors}} @property="description">
<label for="description" class="fw6">Description</label>
<GhTextarea
@id="description"
@name="description"
@stopEnterKeyDownPropagation="true"
/>
<GhErrorMessage @errors={{this.errors}} @property="description" />
</GhFormGroup>
</div>
</div>
<div class="gh-main-section-block no-margin">
<h4 class="gh-main-section-header small bn">Portal preview</h4>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer top-shadow">
<button
class="gh-btn"
{{on "click" @close}}
{{!-- disable mouseDown so it doesn't trigger focus-out validations --}}
{{on "mousedown" (optional this.noop)}}
data-test-button="cancel-offer"
>
<span>Cancel</span>
</button>
<GhTaskButton @buttonText="Add offer"
@successText={{"Added"}}
{{!-- @task={{this.addProduct}} --}}
@class="gh-btn gh-btn-green gh-btn-icon gh-btn-add-memberproduct"
data-test-button="save-comp-product" />
</div>
</section>

View File

@ -1,50 +0,0 @@
import Component from '@glimmer/component';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
export default class ModalsMembershipOfferComponent extends Component {
@service config;
@service settings;
@service store;
@tracked cadences = [];
@tracked products = [];
@tracked selectedDiscountType = 'percentage';
constructor() {
super(...arguments);
}
@task({drop: true})
*fetchProducts() {
this.products = yield this.store.query('product', {include: 'monthly_price,yearly_price,benefits'});
const cadences = [];
this.products.forEach((product) => {
cadences.push({
label: `${product.name} - Monthly`,
name: product.monthlyPrice.id
});
cadences.push({
label: `${product.name} - Yearly`,
name: product.yearlyPrice.id
});
});
this.cadences = cadences;
}
@action
setup() {
this.fetchProducts.perform();
}
@action
setDiscountType(discountType) {
this.selectedDiscountType = discountType;
}
@action
updateVisibility(tab) {
this.tab = tab;
}
}

View File

@ -1,7 +1,51 @@
import Controller from '@ember/controller';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
export default class MembersController extends Controller {
@service config;
@service settings;
@service store;
@tracked cadences = [];
@tracked products = [];
@tracked selectedDiscountType = 'percentage';
constructor() {
super(...arguments);
this.setup();
}
@task({drop: true})
*fetchProducts() {
this.products = yield this.store.query('product', {include: 'monthly_price,yearly_price,benefits'});
const cadences = [];
this.products.forEach((product) => {
cadences.push({
label: `${product.name} - Monthly`,
name: product.monthlyPrice.id
});
cadences.push({
label: `${product.name} - Yearly`,
name: product.yearlyPrice.id
});
});
this.cadences = cadences;
}
@action
save() {}
setup() {
this.fetchProducts.perform();
}
@action
setDiscountType(discountType) {
this.selectedDiscountType = discountType;
}
@action
updateVisibility(tab) {
this.tab = tab;
}
}

View File

@ -44,9 +44,7 @@ Router.map(function () {
this.route('settings');
this.route('settings.general', {path: '/settings/general'});
this.route('settings.membership', {path: '/settings/members'}, function () {
this.route('offer');
});
this.route('settings.membership', {path: '/settings/members'});
this.route('settings.members-email', {path: '/settings/members-email'});
this.route('settings.code-injection', {path: '/settings/code-injection'});

View File

@ -989,6 +989,20 @@
max-width: var(--main-layout-content-maxwidth);
}
.gh-canvas.circle-bg::before {
position: absolute;
display: block;
content: "";
top: -450px;
right: -250px;
width: 970px;
height: 970px;
border-radius: 50%;
z-index: 0;
background: var(--main-color-content-greybg);
opacity: 0.5;
}
.gh-canvas-header {
margin: 0;
position: sticky;
@ -999,6 +1013,11 @@
border-color: transparent;
}
.gh-canvas.circle-bg .gh-canvas-header {
position: relative;
background: none;
}
.gh-canvas-header.grey,
.gh-main-grey .gh-canvas-header {
background: var(--main-color-content-greybg);
@ -1029,6 +1048,25 @@
background: var(--white);
}
/* Main layouts */
.gh-main-layout.content-preview {
display: grid;
grid-template-columns: auto 460px;
grid-gap: 32px;
}
@media (max-width: 1320px) {
.gh-main-layout.content-preview {
grid-gap: 0;
}
}
@media (max-width: 1140px) {
.gh-main-layout.content-preview {
display: flex;
}
}
/* Main sections */
/*
.gh-main-section [columns-2|columns-3]

View File

@ -72,18 +72,50 @@
}
.gh-offer-type {
display: grid;
grid-template-columns: 160px auto;
grid-gap: 24px;
white-space: nowrap;
margin-top: 3px;
margin-top: -8px;
}
.gh-offer-type .gh-radio {
margin-bottom: 16px;
margin-bottom: 0;
align-items: center;
}
.gh-offer-type .gh-radio-content {
margin-left: 6px;
}
.gh-offer-type .gh-radio-label {
font-size: 1.3rem;
}
.gh-offer-modal-content textarea {
.gh-offer-form textarea {
max-width: unset;
}
.gh-offer-form .form-group {
max-width: unset;
}
/* Offers  Portal preview */
.gh-offer-portal-preview {
display: flex;
justify-content: flex-end;
width: 100%;
}
.gh-offer-portal-preview-mock {
display: flex;
align-items: center;
justify-content: center;
color: var(--midgrey);
border-radius: 5px;
height: 640px;
width: 420px;
background: #fff;
box-shadow: var(--box-shadow-preview-box);
}

View File

@ -1435,49 +1435,12 @@ p.theme-validation-details {
}
/* Membership */
.gh-setting-members-header {
position: relative;
background: none;
}
.gh-setting-members-canvas::before {
position: absolute;
display: block;
content: "";
top: -450px;
right: -250px;
width: 970px;
height: 970px;
border-radius: 50%;
z-index: 0;
background: var(--main-color-content-greybg);
opacity: 0.5;
}
@media (max-width: 1140px) {
.gh-setting-members-canvas::before {
display: none;
}
}
.gh-setting-members-basics {
display: grid;
grid-template-columns: auto 460px;
grid-gap: 32px;
}
@media (max-width: 1320px) {
.gh-setting-members-basics {
grid-gap: 0;
}
}
@media (max-width: 1140px) {
.gh-setting-members-basics {
display: flex;
}
}
.gh-setting-members-basicsform {
display: flex;
flex-direction: column;
@ -1630,14 +1593,7 @@ p.theme-validation-details {
align-items: center;
justify-content: center;
background: #fff;
box-shadow:
0 2.8px 2.2px rgba(0, 0, 0, 0.02),
0 6.7px 5.3px rgba(0, 0, 0, 0.028),
0 12.5px 10px rgba(0, 0, 0, 0.035),
0 22.3px 17.9px rgba(0, 0, 0, 0.042),
0 41.8px 33.4px rgba(0, 0, 0, 0.05),
0 100px 80px rgba(0, 0, 0, 0.07)
;
box-shadow: var(--box-shadow-preview-box);
width: 420px;
height: 562px;
margin-bottom: 32px;

View File

@ -125,6 +125,10 @@ input[type=number] {
margin-bottom: 0;
}
.form-group:last-of-type {
margin-bottom: 0;
}
@media (max-width: 550px) {
.form-group {
max-width: 100%;

View File

@ -171,6 +171,15 @@
0 76px 80px -5px rgba(0, 0, 0, 0.04)
;
--box-shadow-preview-box:
0 2.8px 2.2px rgba(0, 0, 0, 0.02),
0 6.7px 5.3px rgba(0, 0, 0, 0.028),
0 12.5px 10px rgba(0, 0, 0, 0.035),
0 22.3px 17.9px rgba(0, 0, 0, 0.042),
0 41.8px 33.4px rgba(0, 0, 0, 0.05),
0 100px 80px rgba(0, 0, 0, 0.07)
;
/* Inputs */
--input-bg-color: var(--white);
--input-border-color: var(--whitegrey-d2);

View File

@ -11,4 +11,4 @@
.shadow-1 { box-shadow: var(--shadow-1); }
.shadow-2 { box-shadow: var(--shadow-2); }
.shadow-3 { box-shadow: var(--shadow-3); }
.shadow-3 { box-shadow: var(--shadow-3); }

View File

@ -1,4 +1,4 @@
<section class="gh-canvas">
<section class="gh-canvas circle-bg">
<GhCanvasHeader class="gh-canvas-header break tablet members-header">
<h2 class="gh-canvas-title" data-test-screen-title>
<LinkTo @route="offers" data-test-link="offers-back">Offers</LinkTo>
@ -11,6 +11,133 @@
</GhCanvasHeader>
<section class="view-container">
Offer...
<div class="gh-main-layout content-preview">
<form>
<div class="gh-main-section gh-offer-form">
<div class="gh-main-section-block no-margin">
<h4 class="gh-main-section-header small bn">Basic</h4>
<div class="gh-main-section-content grey">
<GhFormGroup @errors={{this.errors}} @property="name">
<label for="name" class="fw6">Name</label>
<GhTextInput
@name="name"
@placeholder="Black Friday"
@id="name"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="name" />
<p>Will be shown to members on the Stripe Checkout page</p>
</GhFormGroup>
</div>
<h4 class="gh-main-section-header small bn">Discount info</h4>
<div class="gh-main-section-content grey">
<GhFormGroup @errors={{this.errors}} @property="product-cadence">
<label for="product-cadence" class="fw6">Product cadence</label>
<span class="gh-select">
<OneWaySelect @value={{this.selectedVisibility}}
@options={{this.cadences}}
@optionValuePath="name"
@optionLabelPath="label"
@optionTargetPath="name"
@update={{this.updateVisibility}}
/>
{{svg-jar "arrow-down-small"}}
</span>
<GhErrorMessage @errors={{this.errors}} @property="product-cadence" />
</GhFormGroup>
{{#if (eq this.selectedDiscountType "percentage")}}
<GhFormGroup @errors={{this.errors}} @property="amount">
<label for="amount" class="fw6">Percentage off</label>
<GhTextInput
@type="number"
@name="amount"
@placeholder=""
@id="amount"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="amount" />
</GhFormGroup>
{{else}}
<GhFormGroup @errors={{this.errors}} @property="amount">
<label for="amount" class="fw6">Amount off</label>
<GhTextInput
@name="amount"
@type="number"
@placeholder=""
@id="amount"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="amount" />
</GhFormGroup>
{{/if}}
<div class="gh-offer-type">
<div class="gh-radio {{if (eq this.selectedDiscountType "percentage") "active"}}" {{on "click" (fn this.setDiscountType "percentage")}}>
<div class="gh-radio-button"></div>
<div class="gh-radio-content">
<div class="gh-radio-label">Percentage discount</div>
</div>
</div>
<div class="gh-radio {{if (eq this.selectedDiscountType "fixed") "active"}}" {{on "click" (fn this.setDiscountType "fixed")}}>
<div class="gh-radio-button"></div>
<div class="gh-radio-content">
<div class="gh-radio-label">Fixed amount discount</div>
</div>
</div>
</div>
</div>
<h4 class="gh-main-section-header small bn">Portal settings</h4>
<div class="gh-main-section-content grey">
<div class="form-col2">
<GhFormGroup @errors={{this.errors}} @property="display-title">
<label for="display-title" class="fw6">Display title</label>
<GhTextInput
@name="display-title"
@placeholder="Black Friday Special"
@id="display-title"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="display-title" />
</GhFormGroup>
<GhFormGroup @errors={{this.errors}} @property="code">
<label for="code" class="fw6">Offer code</label>
<GhTextInput
@name="code"
@placeholder="black-friday"
@id="code"
@class="gh-input" />
<GhErrorMessage @errors={{this.errors}} @property="code" />
</GhFormGroup>
</div>
<GhFormGroup @errors={{this.errors}} @property="description">
<label for="description" class="fw6">Description</label>
<GhTextarea
@id="description"
@name="description"
@stopEnterKeyDownPropagation="true"
/>
<GhErrorMessage @errors={{this.errors}} @property="description" />
</GhFormGroup>
</div>
</div>
</div>
</form>
<div class="gh-offer-portal-preview">
<div>
<h4 class="gh-main-section-header small bn">Preview</h4>
<div class="gh-offer-portal-preview-mock">Portal preview</div>
</div>
</div>
</div>
<div class="gh-main-section">
<div class="gh-main-section-block">
<button
type="button"
class="gh-btn gh-btn-red gh-btn-icon"
>
<span>Delete offer</span>
</button>
</div>
</div>
</section>
</section>

View File

@ -1,4 +1,4 @@
<section class="gh-canvas gh-setting-members-canvas" {{did-insert this.setup}}>
<section class="gh-canvas circle-bg" {{did-insert this.setup}}>
<GhCanvasHeader class="gh-canvas-header gh-setting-members-header">
<h2 class="gh-canvas-title" data-test-screen-title>
<LinkTo @route="settings">Settings</LinkTo>
@ -19,7 +19,7 @@
<section class="view-container settings-debug">
<div class="gh-setting-members-basics">
<div class="gh-main-layout content-preview">
<div class="gh-setting-members-basicsform">
<p class="intro">Fund your work with subscription revenue. Connect your Stripe account and offer premium content to your audience. Our creators are already making over $5 million per year, while <strong>Ghost takes 0% payment fees</strong>.</p>
<hr>