Skip to content

Commit

Permalink
Merge branch 'add/components/threats-data-view' into update/component…
Browse files Browse the repository at this point in the history
…s/threats-data-view-actions
  • Loading branch information
dkmyta authored Oct 23, 2024
2 parents 9375f9e + e63ea79 commit 6100588
Show file tree
Hide file tree
Showing 61 changed files with 866 additions and 394 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

AI Client: make reload handler prop optional as it still works in a fuzzy way. The error notice (modal) will instruct to reload the page when the optional prop is not provided
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: fixed

AI Client: fix initial state being mapped even when fetch fails, making the default state nonsensical
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,34 @@ import type React from 'react';

export const FeatureFetchFailureScreen: React.FC< {
onCancel: () => void;
onRetry: () => void;
onRetry?: () => void;
} > = ( { onCancel, onRetry } ) => {
const errorMessage = __(
'We are sorry. There was an error loading your Jetpack AI plan data. Please, try again.',
'jetpack-ai-client'
);

const errorMessageWithoutRetry = __(
'We are sorry. There was an error loading your Jetpack AI plan data. Please, reload the page and try again.',
'jetpack-ai-client'
);

return (
<div className="jetpack-ai-logo-generator-modal__notice-message-wrapper">
<div className="jetpack-ai-logo-generator-modal__notice-message">
<span className="jetpack-ai-logo-generator-modal__loading-message">{ errorMessage }</span>
<span className="jetpack-ai-logo-generator-modal__loading-message">
{ onRetry ? errorMessage : errorMessageWithoutRetry }
</span>
</div>
<div className="jetpack-ai-logo-generator-modal__notice-actions">
<Button variant="tertiary" onClick={ onCancel }>
{ __( 'Cancel', 'jetpack-ai-client' ) }
</Button>
<Button variant="primary" onClick={ onRetry }>
{ __( 'Try again', 'jetpack-ai-client' ) }
</Button>
{ onRetry && (
<Button variant="primary" onClick={ onRetry }>
{ __( 'Try again', 'jetpack-ai-client' ) }
</Button>
) }
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
isOpen,
onClose,
onApplyLogo,
onReload,
onReload = null,
siteDetails,
context,
placement,
Expand Down Expand Up @@ -73,7 +73,8 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
site,
requireUpgrade,
} = useLogoGenerator();
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
const { featureFetchError, setFeatureFetchError, firstLogoPromptFetchError, clearErrors } =
useRequestErrors();
const siteId = siteDetails?.ID;
const [ logoAccepted, setLogoAccepted ] = useState( false );
const { nextTierCheckoutURL: upgradeURL } = useCheckout();
Expand Down Expand Up @@ -105,6 +106,15 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
*/
const initializeModal = useCallback( async () => {
try {
if ( ! siteId ) {
throw new Error( 'Site ID is missing' );
}

if ( ! feature?.featuresControl?.[ 'logo-generator' ]?.enabled ) {
setFeatureFetchError( 'Failed to fetch feature data' );
throw new Error( 'Failed to fetch feature data' );
}

const hasHistory = ! isLogoHistoryEmpty( String( siteId ) );

const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST;
Expand All @@ -125,10 +135,10 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
: currentLimit < currentUsage );

// If the site requires an upgrade, show the upgrade screen immediately.
setNeedsFeature( currentLimit === 0 );
setNeedsFeature( currentValue === 0 );
setNeedsMoreRequests( siteNeedsMoreRequests );

if ( currentLimit === 0 || siteNeedsMoreRequests ) {
if ( currentValue === 0 || siteNeedsMoreRequests ) {
setLoadingState( null );
return;
}
Expand Down Expand Up @@ -179,6 +189,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
isLogoHistoryEmpty,
siteId,
requireUpgrade,
setFeatureFetchError,
] );

const handleModalOpen = useCallback( async () => {
Expand All @@ -201,6 +212,15 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement } );
};

const handleReload = useCallback( () => {
if ( ! onReload ) {
return;
}
closeModal();
requestedFeatureData.current = false;
onReload();
}, [ onReload, closeModal ] );

const handleApplyLogo = ( mediaId: number ) => {
setLogoAccepted( true );
onApplyLogo?.( mediaId );
Expand Down Expand Up @@ -229,7 +249,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
// Handles modal opening logic
useEffect( () => {
// While the modal is not open, the siteId is not set, or the feature data is not available, do nothing.
if ( ! isOpen || ! siteId || ! feature?.costs ) {
if ( ! isOpen ) {
return;
}

Expand All @@ -238,7 +258,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
needsToHandleModalOpen.current = false;
handleModalOpen();
}
}, [ isOpen, siteId, handleModalOpen, feature ] );
}, [ isOpen, handleModalOpen ] );

let body: React.ReactNode;

Expand All @@ -248,10 +268,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
body = (
<FeatureFetchFailureScreen
onCancel={ closeModal }
onRetry={ () => {
closeModal();
onReload?.();
} }
onRetry={ onReload ? handleReload : null }
/>
);
} else if ( needsFeature || needsMoreRequests ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
color: var(--studio-gray-50, #646970);
}

&[data-placeholder]:empty:focus::before {
&[data-placeholder]:empty:focus::before:not([contentEditable="false"]) {
content: "";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
}, [ prompt ] );

useEffect( () => {
if ( imageStyles.length > 0 ) {
if ( imageStyles && imageStyles.length > 0 ) {
// Sort styles to have "None" and "Auto" first
setStyles(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const useLogoGenerator = () => {
const logoGeneratorControl = aiAssistantFeatureData?.featuresControl?.[
'logo-generator'
] as LogoGeneratorFeatureControl;
const imageStyles: Array< ImageStyleObject > = logoGeneratorControl?.styles;
const imageStyles: Array< ImageStyleObject > = logoGeneratorControl?.styles || [];

const generateFirstPrompt = useCallback(
async function (): Promise< string > {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ const actions = {
query: 'force=wpcom',
} );

if ( response.data ) {
throw new Error( 'Failed to fetch' );
}

// Store the feature in the store.
dispatch(
actions.storeAiAssistantFeature( mapAiFeatureResponseToAiFeatureProps( response ) )
Expand Down
10 changes: 10 additions & 0 deletions projects/js-packages/ai-client/src/logo-generator/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,16 @@ export default function reducer(
case ACTION_SET_FEATURE_FETCH_ERROR:
return {
...state,
features: {
...state.features,
aiAssistantFeature: {
...state?.features?.aiAssistantFeature,
_meta: {
...state?.features?.aiAssistantFeature?._meta,
isRequesting: false,
},
},
},
_meta: {
...( state._meta ?? {} ),
featureFetchError: action.error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export type AiAssistantFeatureEndpointResponseProps = {
};
};
'features-control'?: FeaturesControl;
data?: string; // when WP responds with a 200 status code but it's the error wrap
};

export type SaveLogo = ( logo: Logo ) => Promise< { mediaId: number; mediaURL: string } >;
2 changes: 1 addition & 1 deletion projects/js-packages/ai-client/src/logo-generator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface GeneratorModalProps {
isOpen: boolean;
onClose: () => void;
onApplyLogo: ( mediaId: number ) => void;
onReload: () => void;
onReload?: () => void;
context: string;
placement: string;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { __ } from '@wordpress/i18n';

export const PAID_PLUGIN_SUPPORT_URL = 'https://jetpack.com/contact-support/?rel=support';

export const THREAT_STATUSES = [
{ value: 'current', label: __( 'Active', 'jetpack' ) },
{ value: 'fixed', label: __( 'Fixed', 'jetpack' ) },
{ value: 'ignored', label: __( 'Ignored', 'jetpack' ) },
];
export const THREAT_STATUSES: { value: string; label: string; variant?: 'success' | 'warning' }[] =
[
{ value: 'current', label: __( 'Active', 'jetpack' ), variant: 'warning' },
{ value: 'fixed', label: __( 'Fixed', 'jetpack' ), variant: 'success' },
{ value: 'ignored', label: __( 'Ignored', 'jetpack' ) },
];

export const THREAT_TYPES = [
{ value: 'plugin', label: __( 'Plugin', 'jetpack' ) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function FixerStatusIcon( {
<IconTooltip
icon={ info }
iconClassName={ styles[ 'icon-info' ] }
iconSize={ 24 }
iconSize={ size }
text={ createInterpolateElement(
__(
'An error occurred auto-fixing this threat. Please try again or <supportLink>contact support</supportLink>.',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { Icon } from '@wordpress/components';
import {
Action,
DataViews,
Field,
FieldType,
Filter,
filterSortAndPaginate,
SortDirection,
SupportedLayouts,
type View,
} from '@wordpress/dataviews';
import { dateI18n } from '@wordpress/date';
import { __, _x } from '@wordpress/i18n';
import { Icon } from '@wordpress/icons';
import { useCallback, useMemo, useState } from 'react';
import Badge from '../badge';
import { THREAT_STATUSES, THREAT_TYPES } from './constants';
Expand Down Expand Up @@ -62,7 +64,7 @@ export default function ThreatsDataView( {
search: '',
filters: filters || [],
page: 1,
perPage: 25,
perPage: 20,
};

/**
Expand Down Expand Up @@ -209,6 +211,15 @@ export default function ThreatsDataView( {
THREAT_STATUSES.find( ( { value } ) => value === item.status )?.value ?? item.status
);
},
render( { item }: { item: DataViewThreat } ) {
if ( item.status ) {
const status = THREAT_STATUSES.find( ( { value } ) => value === item.status );
if ( status ) {
return <Badge variant={ status?.variant }>{ status?.label }</Badge>;
}
}
return <Badge variant="warning">{ __( 'Active', 'jetpack' ) }</Badge>;
},
},
{
id: 'extension',
Expand Down Expand Up @@ -322,6 +333,44 @@ export default function ThreatsDataView( {
},
]
: [] ),
...( dataFields.includes( 'firstDetected' )
? [
{
id: 'first-detected',
label: __( 'First Detected', 'jetpack' ),
type: 'datetime' as FieldType,
getValue( { item }: { item: DataViewThreat } ) {
return new Date( item.firstDetected );
},
render( { item }: { item: DataViewThreat } ) {
return (
<span className={ styles.threat__firstDetected }>
{ dateI18n( 'F j Y', item.firstDetected, false ) }
</span>
);
},
},
]
: [] ),
...( dataFields.includes( 'fixedOn' )
? [
{
id: 'fixed-on',
label: __( 'Fixed On', 'jetpack' ),
type: 'datetime' as FieldType,
getValue( { item }: { item: DataViewThreat } ) {
return new Date( item.firstDetected );
},
render( { item }: { item: DataViewThreat } ) {
return (
<span className={ styles.threat__fixedOn }>
{ dateI18n( 'F j Y', item.firstDetected, false ) }
</span>
);
},
},
]
: [] ),
];

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Default.args = {
fixedOn: null,
severity: 8,
fixable: { fixer: 'rollback', target: 'January 26, 2024, 6:49 am', extensionStatus: '' },
fixer: { status: 'in_progress', last_updated: new Date().toISOString() },
fixer: { status: 'not_started' },
status: 'current',
filename: '/var/www/html/wp-content/index.php',
context: {
Expand Down Expand Up @@ -94,6 +94,7 @@ Default.args = {
fixedOn: null,
severity: 3,
fixable: { fixer: 'update', target: '1.12.4', extensionStatus: 'inactive' },
fixer: { status: 'in_progress', last_updated: new Date().toISOString() },
status: 'current',
filename: null,
context: null,
Expand Down
Loading

0 comments on commit 6100588

Please sign in to comment.