/** * External dependencies */ import { Button } from '@wordpress/components'; import { createInterpolateElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { Icon, check } from '@wordpress/icons'; import { Link } from '@wordpress/ui'; import clsx from 'clsx'; /** * Internal dependencies */ import './style.scss'; import errorExclamation from '../../icons/error-exclamation.tsx'; import { ERROR_QUOTA_EXCEEDED } from '../../types.ts'; import AiFeedbackThumbs from '../ai-feedback/index.tsx'; /** * Types */ import type { SuggestionErrorCode } from '../../types.ts'; import type { MouseEvent, ReactElement, ReactNode } from 'react'; export const MESSAGE_SEVERITY_WARNING = 'warning'; export const MESSAGE_SEVERITY_ERROR = 'error'; export const MESSAGE_SEVERITY_SUCCESS = 'success'; export const MESSAGE_SEVERITY_INFO = 'info'; export type MessageSeverityProp = | typeof MESSAGE_SEVERITY_WARNING | typeof MESSAGE_SEVERITY_ERROR | typeof MESSAGE_SEVERITY_SUCCESS | typeof MESSAGE_SEVERITY_INFO | null; type AiFeedbackThumbsOptions = { showAIFeedbackThumbs?: boolean; ratedItem?: string; prompt?: string; block?: string | null; onRate?: ( rating: string ) => void; }; export type MessageProps = { icon?: ReactNode; severity?: MessageSeverityProp; aiFeedbackThumbsOptions?: AiFeedbackThumbsOptions; children: ReactNode; }; export type GuidelineMessageProps = { aiFeedbackThumbsOptions?: AiFeedbackThumbsOptions; }; export type OnUpgradeClick = ( event?: MouseEvent< HTMLButtonElement > ) => void; export type UpgradeMessageProps = { requestsRemaining: number; severity?: MessageSeverityProp; onUpgradeClick: OnUpgradeClick; upgradeUrl?: string; }; export type ErrorMessageProps = { error?: string; code?: SuggestionErrorCode; onTryAgainClick: () => void; onUpgradeClick: OnUpgradeClick; upgradeUrl?: string; }; const messageIconsMap = { [ MESSAGE_SEVERITY_INFO ]: null, [ MESSAGE_SEVERITY_WARNING ]: null, [ MESSAGE_SEVERITY_ERROR ]: errorExclamation, [ MESSAGE_SEVERITY_SUCCESS ]: check, }; /** * React component to render a block message. * * @param {MessageProps} props - Component props. * @return {ReactElement} Banner component. */ export default function Message( { severity = MESSAGE_SEVERITY_INFO, icon = null, aiFeedbackThumbsOptions = { showAIFeedbackThumbs: false, ratedItem: '', prompt: '', block: null, onRate: () => {}, }, children, }: MessageProps ): ReactElement { return (
{ ( messageIconsMap[ severity ] || icon ) && ( ) } {
{ children }
} { aiFeedbackThumbsOptions.showAIFeedbackThumbs && aiFeedbackThumbsOptions.prompt && ( ) }
); } /** * React component to render a learn more link. * * @return {ReactElement} - Learn more link component. */ function LearnMoreLink(): ReactElement { return ( { __( 'Learn more', 'jetpack-ai-client' ) } ); } /** * React component to render a guideline message. * * @param {GuidelineMessageProps} props - Component props. * @return {ReactElement} - Message component. */ export function GuidelineMessage( { aiFeedbackThumbsOptions = { showAIFeedbackThumbs: false, ratedItem: '', prompt: '', block: null, onRate: () => {}, }, }: GuidelineMessageProps ): ReactElement { return ( { __( 'AI-generated content could be inaccurate or biased.', 'jetpack-ai-client' ) } ); } /** * React component to render a fair usage limit message. * * @return {ReactElement} - Message component. */ export function FairUsageLimitMessage(): ReactElement { const message = __( "You've reached this month's request limit, per our fair usage policy", 'jetpack-ai-client' ); const element = createInterpolateElement( message, { link: ( ), } ); return { element }; } /** * React component to render an upgrade message for free tier users * * @param {number} requestsRemaining - Number of requests remaining. * @return {ReactElement} - Message component. */ export function UpgradeMessage( { requestsRemaining, severity, onUpgradeClick, upgradeUrl, }: UpgradeMessageProps ): ReactElement { let messageSeverity = severity; if ( messageSeverity == null ) { messageSeverity = requestsRemaining > 0 ? MESSAGE_SEVERITY_INFO : MESSAGE_SEVERITY_WARNING; } return ( { sprintf( // translators: %1$d: number of requests remaining __( 'You have %1$d requests remaining.', 'jetpack-ai-client' ), requestsRemaining ) } ); } /** * React component to render an error message * * @param {number} requestsRemaining - Number of requests remaining. * @return {ReactElement} - Message component. */ export function ErrorMessage( { error, code, onTryAgainClick, onUpgradeClick, upgradeUrl, }: ErrorMessageProps ): ReactElement { const errorMessage = error || __( 'Something went wrong', 'jetpack-ai-client' ); return ( { sprintf( // translators: %1$d: A dynamic error message __( 'Error: %1$s', 'jetpack-ai-client' ), errorMessage ) } { code === ERROR_QUOTA_EXCEEDED ? ( ) : ( ) } ); }