Back

88.88% Statements 16/18
93.33% Branches 14/15
50% Functions 2/4
94.11% Lines 16/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169                                                1x                                       7x             7x                                                                     20x 20x 20x 20x 20x           20x 10x               10x 1x                         9x 2x                 7x                                                           20x                  
import {
	Button,
	Card,
	Link,
	LinkButton,
	Stack,
	Text,
	Spinner,
	VisuallyHidden,
} from '@jetpack-premium-analytics/externals';
import { formatMetricValue } from '@jetpack-premium-analytics/formatters';
import {
	getOverLimitMessage,
	getPlanUpgradeUrl,
	usePlanUsage,
	useTrackEvent,
} from '@jetpack-premium-analytics/widgets-toolkit';
import { ProgressBar } from '@wordpress/components';
import { useCallback } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';
import styles from './stats-settings.module.scss';
import type { JSX } from 'react';
 
const TIERED_BILLING_URL =
	'https://jetpack.com/support/jetpack-stats/free-or-paid/paid-stats-tier-billing/';
 
/**
 * The usage meter: views used against the plan's limit, and when the cycle restarts.
 *
 * @param props             - Component props.
 * @param props.limit       - The plan's billable views limit for the cycle.
 * @param props.usage       - The billable views counted so far this cycle.
 * @param props.daysToReset - Days until the cycle restarts.
 * @return The meter.
 */
function UsageMeter( {
	limit,
	usage = 0,
	daysToReset,
}: {
	limit: number;
	usage?: number;
	daysToReset?: number;
} ): JSX.Element {
	const label = sprintf(
		/* translators: 1: views used in the current cycle, 2: the plan's views limit. */
		__( '%1$s / %2$s views', 'jetpack-premium-analytics-pkg' ),
		formatMetricValue( usage, 'number', { decimals: 0 } ),
		formatMetricValue( limit, 'number', { decimals: 0 } )
	);
 
	return (
		<Stack direction="column" gap="sm">
			<Stack direction="row" justify="space-between" align="baseline" gap="md">
				<Text className={ styles.usageCount }>{ label }</Text>
				{ daysToReset !== undefined && (
					<Text className={ styles.usageReset }>
						{ sprintf(
							/* translators: %d: number of days until the billing cycle resets. */
							_n(
								'Restarts in %d day',
								'Restarts in %d days',
								daysToReset,
								'jetpack-premium-analytics-pkg'
							),
							daysToReset
						) }
					</Text>
				) }
			</Stack>
			{ /* Until the @wordpress/ui update (Automattic/jetpack#53004) ships `Meter`, which fits usage against a limit; switch to it then. */ }
			<ProgressBar
				className={ styles.usageBar }
				value={ Math.min( 100, ( usage / limit ) * 100 ) }
				aria-label={ label }
			/>
		</Stack>
	);
}
 
/**
 * The Settings tab's "Your current plan" card.
 *
 * @return The card.
 */
export function PlanUsageCard(): JSX.Element {
	const { data, isLoading, isError, refetch, limit, overLimitMonths } = usePlanUsage();
	const trackEvent = useTrackEvent();
	const upgradeUrl = getPlanUpgradeUrl();
	const retry = useCallback( () => refetch(), [ refetch ] );
	const trackUpgrade = useCallback(
		() => trackEvent( 'jetpack_premium_analytics_plan_upgrade_click' ),
		[ trackEvent ]
	);
 
	let content: JSX.Element;
	if ( isLoading ) {
		content = (
			<>
				<Spinner />
				<VisuallyHidden role="status">
					{ __( 'Loading your plan usage…', 'jetpack-premium-analytics-pkg' ) }
				</VisuallyHidden>
			</>
		);
	} else if ( ! data && isError ) {
		content = (
			<Stack direction="column" align="start" gap="md">
				<Text>
					{ __(
						"We couldn't load plan usage. Please try again in a moment.",
						'jetpack-premium-analytics-pkg'
					) }
				</Text>
				<Button variant="outline" onClick={ retry }>
					{ __( 'Retry', 'jetpack-premium-analytics-pkg' ) }
				</Button>
			</Stack>
		);
	} else if ( limit === null ) {
		content = (
			<Text>
				{ __(
					"Plan usage isn't available for your current plan.",
					'jetpack-premium-analytics-pkg'
				) }
			</Text>
		);
	} else {
		content = (
			<Stack direction="column" align="start" gap="lg">
				<div className={ styles.usageMeter }>
					<UsageMeter
						limit={ limit }
						usage={ data?.current_usage?.views_count }
						daysToReset={ data?.current_usage?.days_to_reset }
					/>
				</div>
				{ overLimitMonths ? (
					<Text className={ styles.usageWarning }>{ getOverLimitMessage( overLimitMonths ) }</Text>
				) : null }
				<Text className={ styles.usageNote }>
					{ __(
						'Billable views are the page views counted against your plan each cycle. Excluded logged-in role views are not counted.',
						'jetpack-premium-analytics-pkg'
					) }{ ' ' }
					<Link href={ TIERED_BILLING_URL } openInNewTab>
						{ __( 'Learn more', 'jetpack-premium-analytics-pkg' ) }
					</Link>
				</Text>
				{ upgradeUrl && (
					<LinkButton href={ upgradeUrl } onClick={ trackUpgrade }>
						{ __( 'Upgrade', 'jetpack-premium-analytics-pkg' ) }
					</LinkButton>
				) }
			</Stack>
		);
	}
 
	return (
		<Card.Root>
			<Card.Header>
				<Card.Title>{ __( 'Your current plan', 'jetpack-premium-analytics-pkg' ) }</Card.Title>
			</Card.Header>
			<Card.Content>{ content }</Card.Content>
		</Card.Root>
	);
}