diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx
index 76862e085c394..b12d721233324 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/auto-firewall-status.tsx
@@ -17,11 +17,11 @@ export const AutoFirewallStatus = () => {
const {
protect: { wafConfig: wafData },
} = getMyJetpackWindowInitialState();
- const { jetpack_waf_automatic_rules: isAutoFirewallEnabled } = wafData;
+ const { jetpack_waf_automatic_rules: isAutoFirewallEnabled } = wafData || {};
if ( isPluginActive && isSiteConnected ) {
if ( isAutoFirewallEnabled ) {
- return ;
+ return ;
}
return ;
@@ -34,18 +34,18 @@ export const AutoFirewallStatus = () => {
* WafStatus component
*
* @param {PropsWithChildren} props - The component props
- * @param {'success' | 'inactive' | 'off'} props.status - The number of threats
+ * @param {'active' | 'inactive' | 'off'} props.status - The status of the WAF
*
* @returns {ReactElement} rendered component
*/
-function WafStatus( { status }: { status: 'success' | 'inactive' | 'off' } ) {
+function WafStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) {
const slug = 'protect';
const { detail } = useProduct( slug );
- const { hasPaidPlanForProduct: hasProtectPaidPlan } = detail;
+ const { hasPaidPlanForProduct = false } = detail || {};
const tooltipContent = useProtectTooltipCopy();
const { autoFirewallTooltip } = tooltipContent;
- if ( status === 'success' ) {
+ if ( status === 'active' ) {
return (
<>
@@ -70,21 +70,19 @@ function WafStatus( { status }: { status: 'success' | 'inactive' | 'off' } ) {
/>
{ __( 'Inactive', 'jetpack-my-jetpack' ) }
- { ! hasProtectPaidPlan && (
-
- <>
- { autoFirewallTooltip.title }
- { autoFirewallTooltip.text }
- >
-
- ) }
+
+ <>
+ { autoFirewallTooltip.title }
+ { autoFirewallTooltip.text }
+ >
+
>
);
}
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/index.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/index.tsx
index a8e9d937d87ce..bcfbbe7f2a04d 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/index.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/index.tsx
@@ -9,7 +9,7 @@ const ProtectCard: FC< { admin: boolean } > = ( { admin } ) => {
const { recordEvent } = useAnalytics();
const slug = 'protect';
const { detail } = useProduct( slug );
- const { isPluginActive, hasPaidPlanForProduct: hasProtectPaidPlan } = detail;
+ const { isPluginActive, hasPaidPlanForProduct: hasProtectPaidPlan } = detail || {};
/**
* Called when secondary "View" button is clicked.
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx
new file mode 100644
index 0000000000000..4459b3df94736
--- /dev/null
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/logins-blocked-status.tsx
@@ -0,0 +1,144 @@
+import { __ } from '@wordpress/i18n';
+import useProduct from '../../../data/products/use-product';
+import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state';
+import useMyJetpackConnection from '../../../hooks/use-my-jetpack-connection';
+import { isJetpackPluginActive } from '../../../utils/is-jetpack-plugin-active';
+import ShieldOff from './assets/shield-off.svg';
+import ShieldPartial from './assets/shield-partial.svg';
+import { InfoTooltip } from './info-tooltip';
+import { useProtectTooltipCopy } from './use-protect-tooltip-copy';
+import type { ReactElement, PropsWithChildren } from 'react';
+
+export const LoginsBlockedStatus = () => {
+ const slug = 'protect';
+ const { detail } = useProduct( slug );
+ const { isPluginActive: isProtectPluginActive = false } = detail || {};
+ const { isSiteConnected } = useMyJetpackConnection();
+ const {
+ protect: { wafConfig: wafData },
+ } = getMyJetpackWindowInitialState();
+ const { blocked_logins: blockedLoginsCount, brute_force_protection: hasBruteForceProtection } =
+ wafData || {};
+
+ // The Brute Force Protection module is available when either the Jetpack plugin Or the Protect plugin is active.
+ const isPluginActive = isProtectPluginActive || isJetpackPluginActive();
+
+ if ( isPluginActive && isSiteConnected ) {
+ if ( hasBruteForceProtection ) {
+ return ;
+ }
+
+ return ;
+ }
+ if ( isSiteConnected && blockedLoginsCount > 0 ) {
+ // logins have been blocked previoulsy, but either the Jetpack or Protect plugin is not active
+ return ;
+ }
+ return ;
+};
+
+/**
+ * BlockedStatus component
+ *
+ * @param {PropsWithChildren} props - The component props
+ * @param {'active' | 'inactive' | 'off'} props.status - The status of Brute Force Protection
+ *
+ * @returns {ReactElement} rendered component
+ */
+function BlockedStatus( { status }: { status: 'active' | 'inactive' | 'off' } ) {
+ const {
+ protect: { wafConfig: wafData },
+ } = getMyJetpackWindowInitialState();
+ const { blocked_logins: blockedLoginsCount } = wafData || {};
+
+ const tooltipContent = useProtectTooltipCopy();
+ const { blockedLoginsTooltip } = tooltipContent;
+
+ if ( status === 'active' ) {
+ return blockedLoginsCount > 0 ? (
+ { blockedLoginsCount }
+ ) : (
+ <>
+
+
+
+
+ <>
+ { blockedLoginsTooltip.title }
+ { blockedLoginsTooltip.text }
+ >
+
+ >
+ );
+ }
+ if ( status === 'inactive' ) {
+ return (
+ <>
+ { blockedLoginsCount > 0 ? (
+ <>
+
+
+
+ { blockedLoginsCount }
+ >
+ ) : (
+
+
+
+ ) }
+
+ <>
+ { blockedLoginsTooltip.title }
+ { blockedLoginsTooltip.text }
+ >
+
+ >
+ );
+ }
+ return (
+ <>
+
+
+
+ { __( 'Off', 'jetpack-my-jetpack' ) }
+ >
+ );
+}
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx
index 79a0b50099b2b..bf420f7576ac4 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/protect-value-section.tsx
@@ -1,14 +1,10 @@
-import { __, _n, sprintf } from '@wordpress/i18n';
-import { useMemo } from 'react';
import useProduct from '../../../data/products/use-product';
-import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state';
-import { timeSince } from '../../../utils/time-since';
import { AutoFirewallStatus } from './auto-firewall-status';
import { InfoTooltip } from './info-tooltip';
+import { LoginsBlockedStatus } from './logins-blocked-status';
import { ScanAndThreatStatus } from './scan-threats-status';
+import { useLastScanText } from './use-last-scan-text';
import { useProtectTooltipCopy } from './use-protect-tooltip-copy';
-import type { TooltipContent } from './use-protect-tooltip-copy';
-import type { FC } from 'react';
import './style.scss';
@@ -16,76 +12,15 @@ const ProtectValueSection = () => {
const slug = 'protect';
const { detail } = useProduct( slug );
const { isPluginActive = false } = detail || {};
- const {
- plugins,
- themes,
- protect: { scanData },
- } = getMyJetpackWindowInitialState();
- const {
- plugins: fromScanPlugins,
- themes: fromScanThemes,
- num_threats: numThreats = 0,
- last_checked: lastScanTime = null,
- } = scanData;
-
- const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
- const themesCount = fromScanThemes.length || Object.keys( themes ).length;
-
- const timeSinceLastScan = lastScanTime ? timeSince( Date.parse( lastScanTime ) ) : false;
- const lastScanText = useMemo( () => {
- if ( isPluginActive ) {
- if ( timeSinceLastScan ) {
- return sprintf(
- /* translators: %s is how long ago since the last scan took place, i.e.- "17 hours ago" */
- __( 'Last scan: %s', 'jetpack-my-jetpack' ),
- timeSinceLastScan
- );
- }
- return null;
- }
- return (
- sprintf(
- /* translators: %d is the number of plugins installed on the site. */
- _n( '%d plugin', '%d plugins', pluginsCount, 'jetpack-my-jetpack' ),
- pluginsCount
- ) +
- ' ' +
- /* translators: The ampersand symbol here (&) is meaning "and". */
- __( '&', 'jetpack-my-jetpack' ) +
- '\xa0' + // `\xa0` is a non-breaking space.
- sprintf(
- /* translators: %d is the number of themes installed on the site. */
- _n( '%d theme', '%d themes', themesCount, 'jetpack-my-jetpack' ).replace( ' ', '\xa0' ), // `\xa0` is a non-breaking space.
- themesCount
- )
- );
- }, [ isPluginActive, timeSinceLastScan, pluginsCount, themesCount ] );
-
- const tooltipContent = useProtectTooltipCopy( { pluginsCount, themesCount, numThreats } );
-
- return (
-
- );
-};
-
-export default ProtectValueSection;
-
-const ValueSection: FC< {
- isProtectActive: boolean;
- lastScanText?: string;
- tooltipContent: TooltipContent;
-} > = ( { isProtectActive, lastScanText, tooltipContent } ) => {
+ const lastScanText = useLastScanText();
+ const tooltipContent = useProtectTooltipCopy();
const { pluginsThemesTooltip } = tooltipContent;
return (
<>
{ lastScanText &&
{ lastScanText }
}
- { ! isProtectActive && (
+ { ! isPluginActive && (
{
- const {
- protect: { wafConfig: wafData },
- } = getMyJetpackWindowInitialState();
- const { blocked_logins: blockedLoginsCount = 0 } = wafData || {};
-
- return { blockedLoginsCount }
;
-};
+export default ProtectValueSection;
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx
index 443a4ee75ec12..15633df50b79f 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/scan-threats-status.tsx
@@ -11,7 +11,7 @@ import ShieldOff from './assets/shield-off.svg';
import ShieldPartial from './assets/shield-partial.svg';
import ShieldSuccess from './assets/shield-success.svg';
import { InfoTooltip } from './info-tooltip';
-import { useProtectTooltipCopy, type TooltipContent } from './use-protect-tooltip-copy';
+import { useProtectTooltipCopy } from './use-protect-tooltip-copy';
import type { ReactElement, PropsWithChildren } from 'react';
export const ScanAndThreatStatus = () => {
@@ -20,28 +20,17 @@ export const ScanAndThreatStatus = () => {
const { isPluginActive = false, hasPaidPlanForProduct: hasProtectPaidPlan } = detail || {};
const { isSiteConnected } = useMyJetpackConnection();
const {
- plugins,
- themes,
protect: { scanData },
} = getMyJetpackWindowInitialState();
- const {
- plugins: fromScanPlugins,
- themes: fromScanThemes,
- num_threats: numThreats = 0,
- } = scanData;
-
- const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
- const themesCount = fromScanThemes.length || Object.keys( themes ).length;
- const tooltipContent = useProtectTooltipCopy( { pluginsCount, themesCount, numThreats } );
- const { scanThreatsTooltip } = tooltipContent;
+ const { plugins, themes, num_threats: numThreats = 0 } = scanData || {};
const criticalScanThreatCount = useMemo( () => {
- const { core, database, files, num_plugins_threats, num_themes_threats } = scanData;
+ const { core, database, files, num_plugins_threats, num_themes_threats } = scanData || {};
const pluginsThreats = num_plugins_threats
- ? fromScanPlugins.reduce( ( accum, plugin ) => accum.concat( plugin.threats ), [] )
+ ? plugins.reduce( ( accum, plugin ) => accum.concat( plugin.threats ), [] )
: [];
const themesThreats = num_themes_threats
- ? fromScanThemes.reduce( ( accum, theme ) => accum.concat( theme.threats ), [] )
+ ? themes.reduce( ( accum, theme ) => accum.concat( theme.threats ), [] )
: [];
const allThreats = [
...pluginsThreats,
@@ -54,29 +43,25 @@ export const ScanAndThreatStatus = () => {
( accum, threat ) => ( threat.severity >= 5 ? ( accum += 1 ) : accum ),
0
);
- }, [ fromScanPlugins, fromScanThemes, scanData ] );
+ }, [ plugins, themes, scanData ] );
if ( isPluginActive && isSiteConnected ) {
if ( hasProtectPaidPlan ) {
if ( numThreats ) {
return (
-
+
);
}
- return ;
+ return ;
}
return numThreats ? (
-
+
) : (
-
+
);
}
- return ;
+ return ;
};
/**
@@ -85,23 +70,24 @@ export const ScanAndThreatStatus = () => {
* @param {PropsWithChildren} props - The component props
* @param {number} props.numThreats - The number of threats
* @param {number} props.criticalThreatCount - The number of critical threats
- * @param {TooltipContent[ 'scanThreatsTooltip' ]} props.tooltipContent - The number of critical threats
+ *
* @returns {ReactElement} rendered component
*/
function ThreatStatus( {
numThreats,
criticalThreatCount,
- tooltipContent,
}: {
numThreats: number;
criticalThreatCount?: number;
- tooltipContent?: TooltipContent[ 'scanThreatsTooltip' ];
} ) {
const { recordEvent } = useAnalytics();
const useTooltipRef = useRef< HTMLButtonElement >();
const isMobileViewport: boolean = useViewportMatch( 'medium', '<' );
const [ isPopoverVisible, setIsPopoverVisible ] = useState( false );
+ const tooltipContent = useProtectTooltipCopy();
+ const { scanThreatsTooltip } = tooltipContent;
+
const toggleTooltip = useCallback(
() =>
setIsPopoverVisible( prevState => {
@@ -110,8 +96,7 @@ function ThreatStatus( {
page: 'my-jetpack',
feature: 'jetpack-protect',
location: 'scan',
- status: 'alert',
- hasPaidPlan: true,
+ has_paid_plan: true,
threats: numThreats,
} );
}
@@ -152,8 +137,8 @@ function ThreatStatus( {
onClose={ hideTooltip }
>
<>
- { tooltipContent.title }
- { tooltipContent.text }
+ { scanThreatsTooltip.title }
+ { scanThreatsTooltip.text }
>
) }
@@ -179,16 +164,13 @@ function ThreatStatus( {
*
* @param {PropsWithChildren} props - The component props
* @param {'success' | 'partial' | 'off'} props.status - The number of threats
- * @param {TooltipContent[ 'scanThreatsTooltip' ]} props.tooltipContent - The number of critical threats
+ *
* @returns { ReactElement} rendered component
*/
-function ScanStatus( {
- status,
- tooltipContent,
-}: {
- status: 'success' | 'partial' | 'off';
- tooltipContent?: TooltipContent[ 'scanThreatsTooltip' ];
-} ) {
+function ScanStatus( { status }: { status: 'success' | 'partial' | 'off' } ) {
+ const tooltipContent = useProtectTooltipCopy();
+ const { scanThreatsTooltip } = tooltipContent;
+
if ( status === 'success' ) {
return (
<>
@@ -225,14 +207,14 @@ function ScanStatus( {
tracksEventName={ 'protect_card_tooltip_open' }
tracksEventProps={ {
location: 'scan',
- status: 'partial',
+ status: status,
hasPaidPlan: false,
threats: 0,
} }
>
<>
- { tooltipContent.title }
- { tooltipContent.text }
+ { scanThreatsTooltip.title }
+ { scanThreatsTooltip.text }
>
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss
index 8fb0364baa8b5..9c3b0d0970d20 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/style.scss
@@ -52,6 +52,13 @@
font-size: var(--font-body);
line-height: var(--font-title-small);
color: var(--jp-gray-70);
+ a {
+ color: var(--jp-black);
+ text-decoration: underline;
+ }
+ a:hover, a:focus {
+ text-decoration: none;
+ }
}
&__data {
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-last-scan-text.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-last-scan-text.ts
new file mode 100644
index 0000000000000..1cf61f6ce0edf
--- /dev/null
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-last-scan-text.ts
@@ -0,0 +1,55 @@
+import { __, _n, sprintf } from '@wordpress/i18n';
+import { useMemo } from 'react';
+import useProduct from '../../../data/products/use-product';
+import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state';
+import { timeSince } from '../../../utils/time-since';
+
+export const useLastScanText = () => {
+ const slug = 'protect';
+ const { detail } = useProduct( slug );
+ const { isPluginActive = false } = detail || {};
+ const {
+ plugins,
+ themes,
+ protect: { scanData },
+ } = getMyJetpackWindowInitialState();
+ const {
+ plugins: fromScanPlugins,
+ themes: fromScanThemes,
+ last_checked: lastScanTime = null,
+ } = scanData || {};
+
+ const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
+ const themesCount = fromScanThemes.length || Object.keys( themes ).length;
+
+ const timeSinceLastScan = lastScanTime ? timeSince( Date.parse( lastScanTime ) ) : false;
+
+ return useMemo( () => {
+ if ( isPluginActive ) {
+ if ( timeSinceLastScan ) {
+ return sprintf(
+ /* translators: %s is how long ago since the last scan took place, i.e.- "17 hours ago" */
+ __( 'Last scan: %s', 'jetpack-my-jetpack' ),
+ timeSinceLastScan
+ );
+ }
+ return null;
+ }
+ return (
+ sprintf(
+ /* translators: %d is the number of plugins installed on the site. */
+ _n( '%d plugin', '%d plugins', pluginsCount, 'jetpack-my-jetpack' ),
+ pluginsCount
+ ) +
+ ' ' +
+ /* translators: The ampersand symbol here (&) is meaning "and". */
+ __( '&', 'jetpack-my-jetpack' ) +
+ '\xa0' + // `\xa0` is a non-breaking space.
+ sprintf(
+ /* translators: %d is the number of themes installed on the site. */
+ _n( '%d theme', '%d themes', themesCount, 'jetpack-my-jetpack' ).replace( ' ', '\xa0' ), // `\xa0` is a non-breaking space.
+ themesCount
+ )
+ );
+ }, [ isPluginActive, timeSinceLastScan, pluginsCount, themesCount ] );
+};
diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts
index 7fc85ea4dba43..4459d36c006b7 100644
--- a/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts
+++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/protect-card/use-protect-tooltip-copy.ts
@@ -1,8 +1,16 @@
+import { createInterpolateElement } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';
+import { useCallback, useMemo, createElement, type ReactElement } from 'react';
import useProduct from '../../../data/products/use-product';
-import type { ReactElement } from 'react';
+import { getMyJetpackWindowInitialState } from '../../../data/utils/get-my-jetpack-window-state';
+import useAnalytics from '../../../hooks/use-analytics';
+import { isJetpackPluginActive } from '../../../utils/is-jetpack-plugin-active';
-type TooltipType = 'pluginsThemesTooltip' | 'scanThreatsTooltip' | 'autoFirewallTooltip';
+type TooltipType =
+ | 'pluginsThemesTooltip'
+ | 'scanThreatsTooltip'
+ | 'autoFirewallTooltip'
+ | 'blockedLoginsTooltip';
export type TooltipContent = {
[ key in TooltipType ]: {
title: ReactElement | string;
@@ -13,24 +21,131 @@ export type TooltipContent = {
/**
* Gets the translated tooltip copy based on Protect Scan details.
*
- * @param {object} props - React props
- * @param {number} props.pluginsCount - The number of installed plugins on the site.
- * @param {number} props.themesCount - The number of installed themes on the site.
- * @param {number} props.numThreats - The number of detected scan threats on the site.
* @returns {TooltipContent} An object containing each tooltip's title and text content.
*/
-export function useProtectTooltipCopy( {
- pluginsCount = 0,
- themesCount = 0,
- numThreats = 0,
-}: {
- pluginsCount?: number;
- themesCount?: number;
- numThreats?: number;
-} = {} ): TooltipContent {
+export function useProtectTooltipCopy(): TooltipContent {
const slug = 'protect';
const { detail } = useProduct( slug );
- const { hasPaidPlanForProduct: hasProtectPaidPlan } = detail;
+ const { isPluginActive: isProtectPluginActive, hasPaidPlanForProduct: hasProtectPaidPlan } =
+ detail || {};
+ const { recordEvent } = useAnalytics();
+ const {
+ plugins,
+ themes,
+ protect: { scanData, wafConfig: wafData },
+ } = getMyJetpackWindowInitialState();
+ const {
+ plugins: fromScanPlugins,
+ themes: fromScanThemes,
+ num_threats: numThreats = 0,
+ } = scanData || {};
+ const {
+ jetpack_waf_automatic_rules: isAutoFirewallEnabled,
+ blocked_logins: blockedLoginsCount,
+ brute_force_protection: hasBruteForceProtection,
+ } = wafData || {};
+
+ const pluginsCount = fromScanPlugins.length || Object.keys( plugins ).length;
+ const themesCount = fromScanThemes.length || Object.keys( themes ).length;
+
+ const settingsLink = useMemo( () => {
+ if ( isProtectPluginActive ) {
+ return 'admin.php?page=jetpack-protect#/firewall';
+ }
+ return isJetpackPluginActive() ? 'admin.php?page=jetpack#/settings' : null;
+ }, [ isProtectPluginActive ] );
+
+ const trackFirewallSettingsLinkClick = useCallback( () => {
+ recordEvent( 'jetpack_protect_card_tooltip_content_link_click', {
+ page: 'my-jetpack',
+ feature: 'jetpack-protect',
+ location: 'auto-firewall-tooltip',
+ path: settingsLink,
+ } );
+ }, [ recordEvent, settingsLink ] );
+
+ const isBruteForcePluginsActive = isProtectPluginActive || isJetpackPluginActive();
+
+ const blockedLoginsTooltip = useMemo( () => {
+ if ( blockedLoginsCount === 0 ) {
+ if ( hasBruteForceProtection ) {
+ return {
+ title: __( 'Brute Force Protection: Active', 'jetpack-my-jetpack' ),
+ text: __(
+ 'Brute Force Protection is actively blocking malicious login attempts. The number of blocked login attempts will display here soon!',
+ 'jetpack-my-jetpack'
+ ),
+ };
+ }
+ return {
+ title: __( 'Brute Force Protection: Inactive', 'jetpack-my-jetpack' ),
+ text: settingsLink
+ ? createInterpolateElement(
+ sprintf(
+ /* translators: %s is the location/page where the settings are located. Either "firewall settings" or "Jetpack settings". */
+ __(
+ 'Brute Force Protection is disabled and not actively blocking malicious login attempts. Go to %s to activate it.',
+ 'jetpack-my-jetpack'
+ ),
+ isProtectPluginActive ? 'firewall settings' : 'Jetpack settings'
+ ),
+ {
+ a: createElement( 'a', {
+ href: settingsLink,
+ onClick: trackFirewallSettingsLinkClick,
+ } ),
+ }
+ )
+ : __(
+ 'Brute Force Protection is disabled and not actively blocking malicious login attempts.',
+ 'jetpack-my-jetpack'
+ ),
+ };
+ }
+ // blockedLoginsCount is greator than 0 here.
+ if ( ! hasBruteForceProtection ) {
+ if ( ! isBruteForcePluginsActive ) {
+ return {
+ title: __( 'Brute Force Protection: Inactive', 'jetpack-my-jetpack' ),
+ text: __(
+ 'For Brute Force Protection, activate the Jetpack or Protect plugin and enable it in settings.',
+ 'jetpack-my-jetpack'
+ ),
+ };
+ }
+ return {
+ title: __( 'Brute Force Protection: Inactive', 'jetpack-my-jetpack' ),
+ text: settingsLink
+ ? createInterpolateElement(
+ sprintf(
+ /* translators: %s is the location/page where the settings are located. Either "firewall settings" or "Jetpack settings". */
+ __(
+ 'Brute Force Protection is disabled and not actively blocking malicious login attempts. Go to %s to activate it.',
+ 'jetpack-my-jetpack'
+ ),
+ isProtectPluginActive ? 'firewall settings' : 'Jetpack settings'
+ ),
+ {
+ a: createElement( 'a', {
+ href: settingsLink,
+ onClick: trackFirewallSettingsLinkClick,
+ } ),
+ }
+ )
+ : __(
+ 'Brute Force Protection is disabled and not actively blocking malicious login attempts.',
+ 'jetpack-my-jetpack'
+ ),
+ };
+ }
+ }, [
+ blockedLoginsCount,
+ hasBruteForceProtection,
+ isBruteForcePluginsActive,
+ isProtectPluginActive,
+ settingsLink,
+ trackFirewallSettingsLinkClick,
+ ] );
return {
pluginsThemesTooltip: {
@@ -82,9 +197,30 @@ export function useProtectTooltipCopy( {
'jetpack-my-jetpack'
),
},
- autoFirewallTooltip: {
- title: __( 'Auto-Firewall: Inactive', 'jetpack-my-jetpack' ),
- text: __( 'Upgrade required for activation. Manual rules available.', 'jetpack-my-jetpack' ),
- },
+ autoFirewallTooltip:
+ hasProtectPaidPlan && ! isAutoFirewallEnabled
+ ? {
+ title: __( 'Auto-Firewall: Inactive', 'jetpack-my-jetpack' ),
+ text: createInterpolateElement(
+ __(
+ 'You have Auto-Firewall disabled, visit your Protect firewall settings to activate.',
+ 'jetpack-my-jetpack'
+ ),
+ {
+ a: createElement( 'a', {
+ href: settingsLink,
+ onClick: trackFirewallSettingsLinkClick,
+ } ),
+ }
+ ),
+ }
+ : {
+ title: __( 'Auto-Firewall: Inactive', 'jetpack-my-jetpack' ),
+ text: __(
+ 'Upgrade required for activation. Manual rules available.',
+ 'jetpack-my-jetpack'
+ ),
+ },
+ blockedLoginsTooltip: blockedLoginsTooltip,
};
}
diff --git a/projects/packages/my-jetpack/_inc/utils/is-jetpack-plugin-active.ts b/projects/packages/my-jetpack/_inc/utils/is-jetpack-plugin-active.ts
new file mode 100644
index 0000000000000..2b0942d4ba902
--- /dev/null
+++ b/projects/packages/my-jetpack/_inc/utils/is-jetpack-plugin-active.ts
@@ -0,0 +1,13 @@
+import { getMyJetpackWindowInitialState } from '../data/utils/get-my-jetpack-window-state';
+
+/**
+ * Check if the Jetpack plugin is active or not.
+ *
+ * @returns {boolean} Returns true if the Jetpack plugin is active, otherwise false.
+ */
+export const isJetpackPluginActive = () => {
+ const { plugins } = getMyJetpackWindowInitialState() || {};
+ const jetpackPlugin = Object.values( plugins ).find( plugin => plugin?.Name === 'Jetpack' );
+
+ return jetpackPlugin && jetpackPlugin.active;
+};
diff --git a/projects/packages/my-jetpack/changelog/update-mj-protect-card-update-n-refactor b/projects/packages/my-jetpack/changelog/update-mj-protect-card-update-n-refactor
new file mode 100644
index 0000000000000..5fc615be4d69b
--- /dev/null
+++ b/projects/packages/my-jetpack/changelog/update-mj-protect-card-update-n-refactor
@@ -0,0 +1,4 @@
+Significance: patch
+Type: changed
+
+Final minor enhancements to the Protect product card in My Jetpack.