File "RecommendationCard.jsx"
Full Path: /home/fresvfqn/waterdamagerestorationandrepairsmithtown.com/wp-content/plugins/extendify/src/Recommendations/components/RecommendationCard.jsx
File size: 7.74 KB
MIME-type: text/x-java
Charset: utf-8
import { Button } from '@wordpress/components';
import { useEffect, useState, useCallback } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
import { __, _x, sprintf } from '@wordpress/i18n';
import { Icon, check, warning, external } from '@wordpress/icons';
import {
recordActivity,
getRecommendation,
} from '@recommendations/utils/record-activity';
import { recordPluginActivity } from '@shared/api/DataApi';
import { installPlugin, activatePlugin } from '@shared/api/wp';
import { sleep, retryOperation } from '@shared/lib/utils';
export const RecommendationCard = ({
slug: product,
title,
description,
ctaContent,
provider,
image,
ctaType,
ctaPluginSlug,
ctaExternalLink,
ctaInternalLink,
priceTag,
}) => {
useEffect(() => {
recordActivity({ slot: 'plugin-search', event: 'view', product });
}, [product]);
return (
<div
className="flex flex-col rounded border border-gray-300"
data-test="extendify-recommendations-card">
<div className="row-auto grid flex-grow grid-cols-[2fr_3fr] grid-rows-[min-content_1fr] gap-x-5 gap-y-3 border-b border-b-gray-100 p-5 xs:grid-cols-[8rem_1fr]">
<div className="row-span-1 xs:row-span-2">
{image &&
(ctaType === 'plugin' ? (
<a
onClick={() =>
recordActivity({
slot: 'plugin-search',
event: 'click-logo',
product,
})
}
// These WP classes (thickbox open-plugin-details-modal) are needed for the link to open in an iframe like WP does.
className="thickbox open-plugin-details-modal block no-underline"
// The hardcoded iframe dimensions are the default ones, but some WP magic makes them responsive.
href={`${window.extSharedData?.adminUrl}/plugin-install.php?tab=plugin-information&plugin=${ctaPluginSlug}&TB_iframe=true&width=600&height=550`}>
<img
className="w-full xs:min-h-[8rem]"
src={image}
alt={title}
/>
</a>
) : (
<img className="w-full xs:min-h-[8rem]" src={image} alt={title} />
))}
</div>
<div className="flex flex-col justify-center xs:justify-start">
<h3 className="m-0 text-lg leading-tight text-wp-theme-main">
{ctaType === 'plugin' ? (
<a
onClick={() =>
recordActivity({
slot: 'plugin-search',
event: 'click-title',
product,
})
}
// These WP classes (thickbox open-plugin-details-modal) are needed for the link to open in an iframe like WP does.
className="thickbox open-plugin-details-modal no-underline focus:shadow-none"
// The hardcoded iframe dimensions are the default ones, but some WP magic makes them responsive.
href={`${window.extSharedData?.adminUrl}/plugin-install.php?tab=plugin-information&plugin=${ctaPluginSlug}&TB_iframe=true&width=600&height=550`}>
{decodeEntities(title)}
</a>
) : (
decodeEntities(title)
)}
</h3>
<p className="m-0 mt-1 text-xs">
{sprintf(
// translators: %s is a name
_x(
'By %s',
'Preposition for "By Author Name"',
'extendify-local',
),
provider,
)}
</p>
</div>
<p className="col-span-2 m-0 text-sm xs:col-span-1">
{decodeEntities(description)}
</p>
</div>
<div className="flex min-h-14 flex-shrink-0 flex-col items-center justify-center p-3 px-5 xxs:flex-row xxs:justify-end">
{priceTag && (
<p
className="m-0 mb-3 xxs:mb-0 xxs:mr-4"
dangerouslySetInnerHTML={{ __html: decodeEntities(priceTag) }}
/>
)}
{ctaType === 'plugin' && (
<InstallPluginAction
product={product}
ctaContent={decodeEntities(ctaContent)}
ctaPluginSlug={ctaPluginSlug}
/>
)}
{ctaType === 'external-link' && (
<ExternalLinkAction
product={product}
ctaContent={decodeEntities(ctaContent)}
ctaExternalLink={ctaExternalLink}
/>
)}
{ctaType === 'internal-link' && (
<InternalLinkAction
product={product}
ctaContent={decodeEntities(ctaContent)}
ctaInternalLink={ctaInternalLink}
/>
)}
</div>
</div>
);
};
const InstallPluginAction = ({ product, ctaContent, ctaPluginSlug }) => {
const [status, setStatus] = useState('idle');
const [error, setError] = useState(null);
const handleInstall = useCallback(async () => {
recordActivity({
slot: 'plugin-search',
event: 'click-install',
product,
});
try {
setStatus('installing');
await Promise.all([
retryOperation(() => installPlugin(ctaPluginSlug), { maxAttempts: 2 }),
// Sleep makes sure the installing UI is displayed for at least 1 second.
sleep(1000),
]);
} catch (_) {
setError(__('Failed to install the plugin', 'extendify-local'));
setStatus('error');
return;
}
const recommendation = getRecommendation({ product });
recordPluginActivity({
slug: recommendation || product,
source: 'search-recommendation-card',
});
try {
setStatus('activating');
await Promise.all([
retryOperation(() => activatePlugin(ctaPluginSlug), { maxAttempts: 2 }),
// Sleep makes sure the activating UI is displayed for at least 1 second.
sleep(1000),
]);
} catch (_) {
setError(__('Failed to activate the plugin', 'extendify-local'));
setStatus('error');
return;
}
setStatus('activated');
}, [product, ctaPluginSlug]);
const actionText = {
idle: ctaContent,
installing: _x(
'Installing...',
'Plugin installation status',
'extendify-local',
),
activating: _x(
'Activating...',
'Plugin activation status',
'extendify-local',
),
activated: _x('Activated', 'Plugin activation status', 'extendify-local'),
error,
};
if (status === 'error') {
return (
<p className="m-0 flex items-center fill-wp-alert-red text-sm text-wp-alert-red">
<Icon icon={warning} />
{actionText[status]}
</p>
);
}
if (status === 'activated') {
return (
<p className="m-0 flex items-center fill-wp-alert-green text-sm text-wp-alert-green">
<Icon icon={check} />
{actionText[status]}
</p>
);
}
return (
<Button
className="h-auto min-w-24 whitespace-normal break-words rounded-sm bg-wp-theme-main px-3 align-middle text-sm text-design-text shadow-none hover:opacity-90 disabled:opacity-80"
type="button"
variant="secondary"
size="compact"
disabled={status !== 'idle'}
isBusy={status !== 'idle'}
onClick={handleInstall}>
{actionText[status]}
</Button>
);
};
const ExternalLinkAction = ({ product, ctaContent, ctaExternalLink }) => {
const ctaExternalLinkWithPartner = decodeEntities(ctaExternalLink).replace(
'{PARTNERID}',
window.extSharedData?.partnerId,
);
return (
<a
onClick={() =>
recordActivity({
slot: 'plugin-search',
event: 'click-link-external',
product,
})
}
href={ctaExternalLinkWithPartner}
target="_blank"
className="relative flex min-h-8 min-w-24 cursor-pointer items-center justify-center whitespace-normal break-words rounded-sm bg-wp-theme-main fill-design-text py-[6px] pl-3 pr-9 text-center text-sm leading-tight text-design-text no-underline hover:opacity-90 focus:shadow-none">
{ctaContent}
<Icon className="absolute right-3 h-5 w-5" icon={external} />
</a>
);
};
const InternalLinkAction = ({ product, ctaContent, ctaInternalLink }) => {
return (
<a
onClick={() =>
recordActivity({
slot: 'plugin-search',
event: 'click-link-internal',
product,
})
}
href={ctaInternalLink}
className="relative flex min-h-8 min-w-24 cursor-pointer items-center justify-center whitespace-normal break-words rounded-sm bg-wp-theme-main fill-design-text px-3 py-[6px] text-center text-sm leading-tight text-design-text no-underline hover:opacity-90 focus:shadow-none">
{ctaContent}
</a>
);
};