PluginProbe
Extendify / 3.0.5
Extendify v3.0.5
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Assist / components / dashboard / RecommendationCard.jsx

RecommendationCard.jsx in Extendify 3.0.5, at src/Assist/components/dashboard/RecommendationCard.jsx

175 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { recordPluginActivity } from '@shared/api/DataApi';
2 import { activatePlugin, installPlugin } from '@shared/api/wp';
3 import { useActivityStore } from '@shared/state/activity';
4 import { Button } from '@wordpress/components';
5 import { useEffect, useState } from '@wordpress/element';
6 import { __, sprintf } from '@wordpress/i18n';
7 import { caution, check, Icon } from '@wordpress/icons';
8
9 export const RecommendationCard = ({ recommendation }) => {
10 if (recommendation.pluginSlug) {
11 return <InstallCard recommendation={recommendation} />;
12 }
13 return <LinkCard recommendation={recommendation} />;
14 };
15
16 const LinkCard = ({ recommendation }) => {
17 const { by, slug, description, image, title, linkType } = recommendation;
18 const { incrementActivity } = useActivityStore();
19 if (!recommendation?.[linkType]) return null;
20
21 return (
22 <a
23 href={recommendation[linkType]?.replace(
24 '{APEXDOMAIN}',
25 window.extSharedData?.apexDomain ?? '',
26 )}
27 onClick={() => incrementActivity(`recommendations-${slug}`)}
28 target="_blank"
29 className="cursor-pointer rounded-sm border border-gray-200 bg-transparent p-4 text-left text-base no-underline hover:border-design-main hover:bg-gray-50 rtl:text-right"
30 >
31 <div className="h-full w-full">
32 <img
33 className="h-8 w-8 rounded-sm fill-current"
34 alt={
35 by
36 ? /* translators: %s: The name of the company/author */
37 sprintf(__('Logo for %s', 'extendify-local'), by)
38 : undefined
39 }
40 src={image}
41 />
42 <div className="mt-2 font-semibold">{title}</div>
43 {by && <div className="text-sm text-gray-700">{by}</div>}
44 <div className="mt-2 text-sm text-gray-800">{description}</div>
45 </div>
46 </a>
47 );
48 };
49
50 const InstallCard = ({ recommendation }) => {
51 const { by, slug, description, image, title, pluginSlug } = recommendation;
52 const { incrementActivity } = useActivityStore();
53 return (
54 // biome-ignore lint: keep button
55 <div
56 role="button"
57 tabIndex={0}
58 onClick={() => incrementActivity(`recommendations-install-${slug}`)}
59 onKeyDown={(e) => {
60 if (!(e.key === 'Enter' || e.key === ' ')) return;
61 incrementActivity(`recommendations-install-${slug}`);
62 }}
63 className="rounded-sm border border-gray-200 bg-transparent p-4 text-left text-base rtl:text-right"
64 >
65 <div className="h-full w-full">
66 <img
67 className="h-8 w-8 rounded-sm fill-current"
68 alt={
69 by ? sprintf(__('Logo for %s', 'extendify-local'), by) : undefined
70 }
71 src={image}
72 />
73 <div className="mt-2 font-semibold">{title}</div>
74 {by && <div className="text-sm text-gray-700">{by}</div>}
75 <div className="mb-3 mt-2 text-sm text-gray-800">{description}</div>
76 <InstallButton slug={pluginSlug} />
77 </div>
78 </div>
79 );
80 };
81
82 const InstallButton = ({ slug }) => {
83 const [installing, setInstalling] = useState(false);
84 const [status, setStatus] = useState('');
85
86 useEffect(() => {
87 const { installedPlugins, activePlugins } = window.extSharedData;
88 const hasPlugin = (p) => p?.includes(slug);
89 const installed = Object.values(installedPlugins).some(hasPlugin);
90 const active = Object.values(activePlugins).some(hasPlugin);
91 if (installed) setStatus('inactive');
92 if (active) setStatus('active');
93 }, [slug, setStatus]);
94
95 const handleClick = async () => {
96 setInstalling(true);
97 try {
98 await installPlugin(slug);
99 recordPluginActivity({
100 slug,
101 source: 'assist-recommendation-card',
102 });
103 } catch (_) {
104 // Fail silently if the plugin is already installed
105 console.error('Error installing plugin:', _);
106 }
107 try {
108 await activatePlugin(slug);
109 setStatus('active');
110 } catch (_) {
111 setStatus('error');
112 setTimeout(() => {
113 setStatus(status);
114 }, 1500);
115 }
116 setInstalling(false);
117 };
118
119 if (status === 'error') {
120 return (
121 <p
122 className="flex items-center fill-wp-alert-red text-wp-alert-red"
123 style={{ fontSize: '13px' }}
124 >
125 <Icon icon={caution} />
126 {__('Error', 'extendify-local')}
127 </p>
128 );
129 }
130
131 if (status === 'active') {
132 return (
133 <p
134 className="flex items-center fill-wp-alert-green text-wp-alert-green"
135 style={{ fontSize: '13px' }}
136 >
137 <Icon icon={check} />
138 {__('Active', 'extendify-local')}
139 </p>
140 );
141 }
142
143 if (status === 'inactive') {
144 return (
145 <Button
146 onClick={handleClick}
147 type="button"
148 variant="secondary"
149 size="compact"
150 disabled={installing}
151 isBusy={installing}
152 >
153 {installing
154 ? __('Activating...', 'extendify-local')
155 : __('Activate', 'extendify-local')}
156 </Button>
157 );
158 }
159
160 return (
161 <Button
162 onClick={handleClick}
163 type="button"
164 variant="secondary"
165 size="compact"
166 disabled={installing}
167 isBusy={installing}
168 >
169 {installing
170 ? __('Installing...', 'extendify-local')
171 : __('Install Now', 'extendify-local')}
172 </Button>
173 );
174 };
175