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:
Ronald Langeveld 2023-11-22 15:18:04 +02:00 committed by GitHub
parent 6b46c828e7
commit cc6e881342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 48 deletions

View File

@ -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>
);

View File

@ -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;