mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 11:55:01 +03:00
Wired latest offer cards to Offers settings group in Admin. (#19095)
refs https://github.com/TryGhost/Product/issues/4176 - wired up Offer data points to the offer cards in the grouped setting.
This commit is contained in:
parent
6b46c828e7
commit
cc6e881342
@ -2,22 +2,63 @@ import React from 'react';
|
||||
import TopLevelGroup from '../../TopLevelGroup';
|
||||
import {Button, withErrorBoundary} from '@tryghost/admin-x-design-system';
|
||||
import {CopyLinkButton} from './offers/OffersIndex';
|
||||
import {Tier, getPaidActiveTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers';
|
||||
import {checkStripeEnabled} from '@tryghost/admin-x-framework/api/settings';
|
||||
import {createRedemptionFilterUrl, getOfferDiscount} from './offers/OffersIndex';
|
||||
import {useBrowseOffers} from '@tryghost/admin-x-framework/api/offers';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
const OfferContainer: React.FC<{offerTitle: string, tier: Tier, cadence: string, redemptions: number, type: string, amount: number, currency: string, offerId: string, offerCode: string, goToOfferEdit: (offerId: string) => void}> = (
|
||||
{offerTitle, tier, cadence, redemptions, type, amount, currency, offerId, offerCode, goToOfferEdit}) => {
|
||||
const {discountColor, discountOffer} = getOfferDiscount(type, amount, cadence, currency || 'USD', tier);
|
||||
return <div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800' onClick={() => goToOfferEdit(offerId)}>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>{offerTitle}</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className={`text-sm font-semibold uppercase ${discountColor}`}>{discountOffer}</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>{tier.name}</span>
|
||||
<span className='text-grey-700'>{cadence}</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href={createRedemptionFilterUrl(offerId)}>{redemptions} redemptions</a>
|
||||
<CopyLinkButton offerCode={offerCode} />
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {updateRoute} = useRouting();
|
||||
const {settings, config} = useGlobalData();
|
||||
|
||||
const {data: {offers: allOffers = []} = {}} = useBrowseOffers();
|
||||
|
||||
const {data: {tiers: allTiers} = {}} = useBrowseTiers();
|
||||
const paidActiveTiers = getPaidActiveTiers(allTiers || []);
|
||||
|
||||
const activeOffers = allOffers.filter(offer => offer.status === 'active');
|
||||
|
||||
activeOffers.sort((a, b) => {
|
||||
const dateA = a.created_at ? new Date(a.created_at) : new Date(0);
|
||||
const dateB = b.created_at ? new Date(b.created_at) : new Date(0);
|
||||
return dateB.getTime() - dateA.getTime();
|
||||
});
|
||||
|
||||
const latestThree = activeOffers.slice(0, 3);
|
||||
|
||||
const openModal = () => {
|
||||
updateRoute('offers/edit');
|
||||
};
|
||||
|
||||
const goToOfferEdit = (offerId: string) => {
|
||||
updateRoute(`offers/edit/${offerId}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<TopLevelGroup
|
||||
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. <a className='text-green' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">Learn more</a></>}
|
||||
description={<>Grow your audience by providing fixed or percentage discounts. {allOffers.length === 0 && <a className='text-green' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">Learn more</a>}</>}
|
||||
keywords={keywords}
|
||||
navid='offers'
|
||||
testId='offers'
|
||||
@ -25,52 +66,31 @@ const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
>
|
||||
<div>
|
||||
<div className='grid grid-cols-3 gap-4'>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Black Friday</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-green'>20% off</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Bronze</span>
|
||||
<span className='text-grey-700'>monthly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">4 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Cyber Monday</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-pink'>7 days free</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Silver</span>
|
||||
<span className='text-grey-700'>yearly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">0 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Great Deal</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-blue'>$20 off</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Bronze</span>
|
||||
<span className='text-grey-700'>yearly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">3 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
latestThree.map((offer) => {
|
||||
const offerTier = paidActiveTiers.find(tier => tier.id === offer?.tier.id);
|
||||
if (!offerTier) {
|
||||
return null;
|
||||
}
|
||||
return <OfferContainer
|
||||
key={offer.id}
|
||||
amount={offer.amount}
|
||||
cadence={offer.cadence}
|
||||
currency={offer.currency || 'USD'}
|
||||
goToOfferEdit={goToOfferEdit}
|
||||
offerCode={offer.code}
|
||||
offerId={offer.id}
|
||||
offerTitle={offer.name}
|
||||
redemptions={offer.redemption_count}
|
||||
tier={offerTier}
|
||||
type={offer.type}
|
||||
/>;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className='mt-4 border-t border-t-grey-200 pt-2'>
|
||||
{allOffers.length > 3 && <div className='mt-4 border-t border-t-grey-200 pt-2'>
|
||||
<Button className='text-sm font-bold text-green' label='Show all' size='sm' link unstyled onClick={openModal} />
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</TopLevelGroup>
|
||||
);
|
||||
|
@ -21,15 +21,15 @@ export const createRedemptionFilterUrl = (id: string): string => {
|
||||
return `${baseHref}?filter=${encodeURIComponent('offer_redemptions:' + id)}`;
|
||||
};
|
||||
|
||||
const getOfferCadence = (cadence: string): string => {
|
||||
export const getOfferCadence = (cadence: string): string => {
|
||||
return cadence === 'month' ? 'monthly' : 'yearly';
|
||||
};
|
||||
|
||||
const getOfferDuration = (duration: string): string => {
|
||||
export const getOfferDuration = (duration: string): string => {
|
||||
return (duration === 'once' ? 'First payment' : duration === 'repeating' ? 'Repeating' : 'Forever');
|
||||
};
|
||||
|
||||
const getOfferDiscount = (type: string, amount: number, cadence: string, currency: string, tier: Tier | undefined): {discountColor: string, discountOffer: string, originalPriceWithCurrency: string, updatedPriceWithCurrency: string} => {
|
||||
export const getOfferDiscount = (type: string, amount: number, cadence: string, currency: string, tier: Tier | undefined): {discountColor: string, discountOffer: string, originalPriceWithCurrency: string, updatedPriceWithCurrency: string} => {
|
||||
let discountColor = '';
|
||||
let discountOffer = '';
|
||||
const originalPrice = cadence === 'month' ? tier?.monthly_price ?? 0 : tier?.yearly_price ?? 0;
|
||||
|
Loading…
Reference in New Issue
Block a user