/* * External dependencies */ import getRedirectUrl from '@automattic/jetpack-components/tools/jp-redirect'; import { useAnalytics, canUserPurchasePlan } from '@automattic/jetpack-shared-extension-utils'; import { Nudge as StandardNudge } from '@automattic/jetpack-shared-extension-utils/components'; import { Notice } from '@wordpress/components'; import { createInterpolateElement, useCallback } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import debugFactory from 'debug'; /* * Internal dependencies */ import useAICheckout from '../../hooks/use-ai-checkout/index.ts'; import useAiFeature from '../../hooks/use-ai-feature/index.ts'; import { LightNudge } from './light-nudge.tsx'; import type { ReactElement } from 'react'; import './style.scss'; type QuotaExceededMessageProps = { placement?: string; description?: string; useLightNudge?: boolean; }; const debug = debugFactory( 'jetpack-ai-client:upgrade-prompt' ); /** * The fair usage notice message for the AI Assistant block. * @return {ReactElement} the fair usage notice message, with the proper link and date. */ const useFairUsageNoticeMessage = () => { const { usagePeriod } = useAiFeature(); const getFormattedUsagePeriodStartDate = planUsagePeriod => { if ( ! planUsagePeriod?.nextStart ) { return null; } const nextUsagePeriodStartDate = new Date( planUsagePeriod.nextStart ); return ( nextUsagePeriodStartDate.toLocaleString( 'default', { month: 'long' } ) + ' ' + nextUsagePeriodStartDate.getDate() ); }; const getFairUsageNoticeMessage = resetDateString => { const fairUsageMessage = __( "You've reached this month's request limit, per our fair usage policy.", 'jetpack-ai-client' ); if ( ! resetDateString ) { return fairUsageMessage; } // Translators: %s is the date when the requests will reset. const dateMessage = __( 'Requests will reset on %s.', 'jetpack-ai-client' ); const formattedDateMessage = sprintf( dateMessage, resetDateString ); return `${ fairUsageMessage } ${ formattedDateMessage }`; }; const nextUsagePeriodStartDateString = getFormattedUsagePeriodStartDate( usagePeriod ); // Get the proper template based on the presence of the next usage period start date. const fairUsageNoticeMessage = getFairUsageNoticeMessage( nextUsagePeriodStartDateString ); const fairUsageNoticeMessageElement = createInterpolateElement( fairUsageNoticeMessage, { link: ( ), span: , } ); return fairUsageNoticeMessageElement; }; /** * The default upgrade prompt for the AI Assistant block, containing the Upgrade button and linking * to the checkout page or the Jetpack AI interstitial page. * * @param {QuotaExceededMessageProps} props - Component props. * @return {ReactElement} the Nudge component with the prompt. */ const DefaultUpgradePrompt = ( { placement = null, description = null, useLightNudge = false, }: QuotaExceededMessageProps ): ReactElement => { const Nudge = useLightNudge ? LightNudge : StandardNudge; const { checkoutUrl } = useAICheckout(); const canUpgrade = canUserPurchasePlan(); const { nextTier, tierPlansEnabled, currentTier, requestsCount } = useAiFeature(); const { tracks } = useAnalytics(); const handleUpgradeClick = useCallback( () => { debug( 'upgrade', placement ); tracks.recordEvent( 'jetpack_ai_upgrade_button', { current_tier_slug: currentTier?.slug, requests_count: requestsCount, placement: placement, } ); }, [ currentTier, requestsCount, tracks, placement ] ); const handleContactUsClick = useCallback( () => { debug( 'contact us', placement ); tracks.recordEvent( 'jetpack_ai_upgrade_contact_us', { placement: placement, } ); }, [ tracks, placement ] ); if ( ! canUpgrade ) { const cantUpgradeDescription = createInterpolateElement( __( 'Congratulations on exploring Jetpack AI and reaching the free requests limit! Reach out to the site administrator to upgrade and keep using Jetpack AI.', 'jetpack-ai-client' ), { strong: , } ); return ( ); } if ( tierPlansEnabled ) { if ( ! nextTier ) { const contactHref = getRedirectUrl( 'jetpack-ai-tiers-more-requests-contact' ); const contactUsDescription = __( 'You have reached the request limit for your current plan.', 'jetpack-ai-client' ); return ( ); } const upgradeDescription = createInterpolateElement( sprintf( /* Translators: %d: the number of requests allowed */ __( 'You have reached the requests limit for your current plan. Upgrade now to increase your requests limit to %d.', 'jetpack-ai-client' ), nextTier.limit ), { strong: , } ); return ( ); } return ( Upgrade now to keep using it.', 'jetpack-ai-client' ), { strong: , } ) } goToCheckoutPage={ handleUpgradeClick } visible={ true } align={ null } title={ null } context={ null } target="_blank" /> ); }; /** * The VIP upgrade prompt, with a single text message recommending that the user reach * out to their VIP account team. * * @param {object} props - Component props. * @param {string} props.description - The description to display in the prompt. * @param {boolean} props.useLightNudge - Wheter to use the light variant of the nudge, or the standard one. * @return {ReactElement} the Nudge component with the prompt. */ const VIPUpgradePrompt = ( { description = null, useLightNudge = false, }: { description?: string; useLightNudge?: boolean; } ): ReactElement => { const Nudge = useLightNudge ? LightNudge : StandardNudge; const vipDescription = createInterpolateElement( __( "You've reached the Jetpack AI rate limit. Please reach out to your VIP account team.", 'jetpack-ai-client' ), { strong: , } ); return ( ); }; type FairUsageNoticeProps = { variant?: 'error' | 'muted'; }; /** * The fair usage notice component. * @param {FairUsageNoticeProps} props - Fair usage notice component props. * @param {FairUsageNoticeProps.variant} props.variant - The variant of the notice to render. * @return {ReactElement} the Notice component with the fair usage message. */ export const FairUsageNotice = ( { variant = 'error' }: FairUsageNoticeProps ) => { const useFairUsageNoticeMessageElement = useFairUsageNoticeMessage(); if ( variant === 'muted' ) { return ( { useFairUsageNoticeMessageElement } ); } if ( variant === 'error' ) { return ( { useFairUsageNoticeMessageElement } ); } return null; }; const QuotaExceededMessage = props => { const { upgradeType, currentTier } = useAiFeature(); // Return notice component for the fair usage limit message, on unlimited plans. if ( currentTier?.value === 1 ) { return ; } // If the user is on a VIP site, show the VIP upgrade prompt. if ( upgradeType === 'vip' ) { return VIPUpgradePrompt( { description: props.description, useLightNudge: props?.useLightNudge, } ); } return DefaultUpgradePrompt( props ); }; export default QuotaExceededMessage;