mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Added static version of Offers list and new screens to AdminX (#18812)
This commit is contained in:
parent
62ecb1ebd8
commit
7904d1c832
5 changed files with 188 additions and 4 deletions
|
@ -0,0 +1 @@
|
|||
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="m2.109375 20.390625 18.28125 -18.28125Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M14.765625 17.578125a2.8125 2.8125 0 1 0 5.625 0 2.8125 2.8125 0 1 0 -5.625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M2.109375 4.921875a2.8125 2.8125 0 1 0 5.625 0 2.8125 2.8125 0 1 0 -5.625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>
|
After Width: | Height: | Size: 645 B |
|
@ -109,7 +109,7 @@ const Sidebar: React.FC = () => {
|
|||
<SettingNavItem icon='emailfield' keywords={membershipSearchKeywords.embedSignupForm} navid='embed-signup-form' title="Embeddable signup form" onClick={handleSectionClick} />
|
||||
{hasRecommendations && <SettingNavItem icon='heart' keywords={membershipSearchKeywords.recommendations} navid='recommendations' title="Recommendations" onClick={handleSectionClick} />}
|
||||
<SettingNavItem icon='baseline-chart' keywords={membershipSearchKeywords.analytics} navid='analytics' title="Analytics" onClick={handleSectionClick} />
|
||||
{hasOffers && <SettingNavItem icon='ellipsis' keywords={membershipSearchKeywords.offers} navid='offers' title="Offers" onClick={handleSectionClick} />}
|
||||
{hasOffers && <SettingNavItem icon='discount' keywords={membershipSearchKeywords.offers} navid='offers' title="Offers" onClick={handleSectionClick} />}
|
||||
</SettingNavSection>
|
||||
|
||||
<SettingNavSection keywords={Object.values(emailSearchKeywords).flat()} title="Email newsletter">
|
||||
|
|
|
@ -17,7 +17,7 @@ const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
return (
|
||||
<SettingGroup
|
||||
customButtons={<Button color='green' disabled={!checkStripeEnabled(settings, config)} label='Manage offers' link linkWithPadding onClick={openModal}/>}
|
||||
description='Grow your audience by providing fixed or percentage discounts. [Learn more]'
|
||||
description={<>Grow your audience by providing fixed or percentage discounts. <a className='text-green' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">Learn more</a></>}
|
||||
keywords={keywords}
|
||||
navid='offers'
|
||||
testId='offers'
|
||||
|
|
|
@ -1,9 +1,126 @@
|
|||
import Form from '../../../../admin-x-ds/global/form/Form';
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import Select from '../../../../admin-x-ds/global/form/Select';
|
||||
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {ReactComponent as CheckIcon} from '../../../../admin-x-ds/assets/icons/check.svg';
|
||||
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
interface OfferType {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const ButtonSelect: React.FC<{type: OfferType, checked: boolean}> = ({type, checked}) => {
|
||||
const checkboxClass = checked ? 'bg-black text-white' : 'border border-grey-300';
|
||||
|
||||
return (
|
||||
<button className='text-left' type='button'>
|
||||
<div className='flex gap-3'>
|
||||
<div className={`mt-0.5 flex h-4 w-4 items-center justify-center rounded-full ${checkboxClass}`}>
|
||||
{checked ? <CheckIcon className='w-2 stroke-[4]' /> : null}
|
||||
</div>
|
||||
<div className='flex flex-col'>
|
||||
<span>{type.title}</span>
|
||||
<span className='text-sm'>{type.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const Sidebar: React.FC = () => {
|
||||
const typeOptions = [
|
||||
{title: 'Discount', description: 'Offer a special reduced price'},
|
||||
{title: 'Free trial', description: 'Give free access for a limited time'}
|
||||
];
|
||||
|
||||
const tierCadenceOptions = [
|
||||
{value: '1', label: 'Bronze — Monthly'},
|
||||
{value: '2', label: 'Bronze — Yearly'},
|
||||
{value: '3', label: 'Silver — Monthly'},
|
||||
{value: '4', label: 'Silver — Yearly'},
|
||||
{value: '5', label: 'Gold — Monthly'},
|
||||
{value: '6', label: 'Gold — Yearly'}
|
||||
];
|
||||
|
||||
const amountOptions = [
|
||||
{value: '1', label: '%'},
|
||||
{value: '2', label: 'USD'}
|
||||
];
|
||||
|
||||
const durationOptions = [
|
||||
{value: '1', label: 'First-payment'},
|
||||
{value: '2', label: 'Multiple-months'},
|
||||
{value: '3', label: 'Forever'}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className='pt-7'>
|
||||
<Form>
|
||||
<TextField
|
||||
hint='Visible to members on Stripe Checkout page.'
|
||||
placeholder='Black Friday'
|
||||
title='Name'
|
||||
/>
|
||||
<section className='mt-4'>
|
||||
<h2 className='mb-4 text-lg'>Offer details</h2>
|
||||
<div className='flex flex-col gap-6'>
|
||||
<div className='flex flex-col gap-4 rounded-md border border-grey-200 p-4'>
|
||||
<ButtonSelect checked={true} type={typeOptions[0]} />
|
||||
<ButtonSelect checked={false} type={typeOptions[1]} />
|
||||
</div>
|
||||
<Select
|
||||
options={tierCadenceOptions}
|
||||
selectedOption={tierCadenceOptions[0]}
|
||||
title='Tier — Cadence'
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<div className='relative'>
|
||||
<TextField title='Amount off' type='number' />
|
||||
<div className='absolute bottom-0 right-1.5 z-10 w-10'>
|
||||
<Select
|
||||
clearBg={true}
|
||||
controlClasses={{menu: 'w-20 right-0'}}
|
||||
options={amountOptions}
|
||||
selectedOption={amountOptions[0]}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Select
|
||||
options={durationOptions}
|
||||
selectedOption={durationOptions[0]}
|
||||
title='Duration'
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className='mt-4'>
|
||||
<h2 className='mb-4 text-lg'>Portal Settings</h2>
|
||||
<div className='flex flex-col gap-6'>
|
||||
<TextField
|
||||
placeholder='Black Friday Special'
|
||||
title='Display title'
|
||||
/>
|
||||
<TextField
|
||||
placeholder='black-friday'
|
||||
title='Offer code'
|
||||
/>
|
||||
<TextArea
|
||||
placeholder='Take advantage of this limited-time offer.'
|
||||
title='Display description'
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddOfferModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
|
@ -16,7 +133,9 @@ const AddOfferModal = () => {
|
|||
}
|
||||
}, [hasOffers, modal, updateRoute]);
|
||||
|
||||
return <PreviewModalContent sidebar={<></>} title='Add Offer' />;
|
||||
const sidebar = <Sidebar />;
|
||||
|
||||
return <PreviewModalContent deviceSelector={false} okLabel='Publish' sidebar={sidebar} size='full' title='Offer' />;
|
||||
};
|
||||
|
||||
export default NiceModal.create(AddOfferModal);
|
||||
|
|
|
@ -1,9 +1,46 @@
|
|||
import Button from '../../../../admin-x-ds/global/Button';
|
||||
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import TabView, {Tab} from '../../../../admin-x-ds/global/TabView';
|
||||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
export type OfferType = 'percent' | 'fixed' | 'trial';
|
||||
|
||||
const OfferCard: React.FC<{name: string, type: OfferType}> = ({name, type}) => {
|
||||
let discountColor = '';
|
||||
|
||||
switch (type) {
|
||||
case 'percent':
|
||||
discountColor = 'text-green';
|
||||
break;
|
||||
case 'fixed':
|
||||
discountColor = 'text-blue';
|
||||
break;
|
||||
case 'trial':
|
||||
discountColor = 'text-pink';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return <div className='flex flex-col items-center gap-6 border border-transparent bg-grey-100 p-5 text-center transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<h2 className='text-[1.6rem]'>{name}</h2>
|
||||
<div className=''>
|
||||
<div className='flex gap-3 text-sm uppercase leading-none'>
|
||||
<span className={`font-semibold ${discountColor}`}>10% off</span>
|
||||
<span className='text-grey-700 line-through'>$5</span>
|
||||
</div>
|
||||
<span className='text-3xl font-bold'>$4</span>
|
||||
</div>
|
||||
<div className='flex flex-col items-center text-xs'>
|
||||
<span className='font-medium'>Bronze monthly — First payment</span>
|
||||
<a className='text-grey-700 hover:underline' href="/ghost/#/members">4 redemptions</a>
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const OffersModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
|
@ -16,7 +53,34 @@ const OffersModal = () => {
|
|||
}
|
||||
}, [hasOffers, modal, updateRoute]);
|
||||
|
||||
return <Modal title='Offers' />;
|
||||
let offersTabs: Tab[] = [
|
||||
{id: 'active', title: 'Active'},
|
||||
{id: 'archived', title: 'Archived'}
|
||||
];
|
||||
|
||||
return <Modal cancelLabel='' header={false} size='lg'>
|
||||
<div className='pt-6'>
|
||||
<header>
|
||||
<div className='flex items-center justify-between'>
|
||||
<TabView
|
||||
border={false}
|
||||
selectedTab='active'
|
||||
tabs={offersTabs}
|
||||
width='wide'
|
||||
onTabChange={() => {}}
|
||||
/>
|
||||
<Button color='green' icon='add' iconColorClass='green' label='New offer' link={true} size='sm' />
|
||||
</div>
|
||||
<h1 className='mt-12 border-b border-b-grey-300 pb-2.5 text-3xl'>Active offers</h1>
|
||||
</header>
|
||||
<div className='mt-8 grid grid-cols-3 gap-6'>
|
||||
<OfferCard name='Black friday' type='percent' />
|
||||
<OfferCard name='Buy this right now' type='fixed' />
|
||||
<OfferCard name='Desperate Sale!' type='trial' />
|
||||
</div>
|
||||
<a className='absolute bottom-10 text-sm' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">→ Learn about offers in Ghost</a>
|
||||
</div>
|
||||
</Modal>;
|
||||
};
|
||||
|
||||
export default NiceModal.create(OffersModal);
|
||||
|
|
Loading…
Add table
Reference in a new issue