PluginProbe ʕ •ᴥ•ʔ
Starter Sites & Templates by Neve / trunk
Starter Sites & Templates by Neve vtrunk
1.4.0 1.3.0 1.2.29 1.2.28 1.2.6 1.2.7 1.2.8 1.2.9 trunk 1.0.10 1.0.11 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.29 1.1.3 1.1.30 1.1.31 1.1.32 1.1.33 1.1.34 1.1.35 1.1.36 1.1.37 1.1.38 1.1.39 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.3 1.2.4 1.2.5
templates-patterns-collection / onboarding / src / Components / FeaturesList.js
templates-patterns-collection / onboarding / src / Components Last commit date
CustomizeControls 2 months ago Steps 2 weeks ago App.js 2 years ago CategoryButtons.js 2 weeks ago CustomTooltip.js 2 years ago EditorSelector.js 2 years ago FeaturesList.js 1 year ago Filters.js 2 weeks ago Header.js 2 weeks ago ImportError.js 2 years ago ImportForm.js 1 year ago ImportMock.js 2 years ago ImportProgress.js 1 year ago Onboarding.js 2 weeks ago ProgressBar.js 2 years ago Search.js 2 weeks ago SitePreview.js 2 years ago SiteSettings.js 2 months ago Sites.js 2 weeks ago StarterSiteCard.js 2 weeks ago Toast.js 2 weeks ago WelcomeMock.js 2 years ago
FeaturesList.js
249 lines
1 import { useState, useEffect } from '@wordpress/element';
2 import { __ } from '@wordpress/i18n';
3
4 const MAX_FEATURE_LIST_LENGTH = 6;
5
6 const decodeHtmlEntities = (str) => {
7 const textArea = document.createElement('textarea');
8 textArea.innerHTML = str;
9 return textArea.value;
10 };
11
12 /**
13 * Plugins to promote.
14 */
15 const featuredPluginCollection = [
16 {
17 id: 'pageBuilder',
18 pluginSlug: 'otter-blocks',
19 label: __('Site Builder', 'templates-patterns-collection'),
20 description: __('Build beautiful pages with a simple drag-and-drop page builder.', 'templates-patterns-collection')
21 },
22 {
23 id: 'contactForm',
24 pluginSlug: 'otter-blocks',
25 label: __('Contact Form', 'templates-patterns-collection'),
26 description: __('Create forms to capture leads and feedback.', 'templates-patterns-collection')
27 },
28 {
29 id: 'imageOpt',
30 pluginSlug: 'optimole-wp',
31 label: __('Image Optimization', 'templates-patterns-collection'),
32 description: __('Make your site faster with automatic image optimization.', 'templates-patterns-collection')
33 },
34 {
35 id: 'caching',
36 pluginSlug: 'wp-cloudflare-page-cache',
37 label: __('Caching', 'templates-patterns-collection'),
38 description: __('Supercharge your site’s speed with powerful caching.', 'templates-patterns-collection')
39 },
40 {
41 id: 'eCommerce',
42 pluginSlug: 'wp-full-stripe-free',
43 label: __('eCommerce', 'templates-patterns-collection'),
44 description: __('Set up your store and start accepting payments via Stripe.', 'templates-patterns-collection'),
45 },
46 {
47 id: 'chatbot',
48 pluginSlug: 'hyve-lite',
49 label: __('AI ChatBot', 'templates-patterns-collection'),
50 description: __('Keep visitors engaged with an intelligent AI chatbot.', 'templates-patterns-collection'),
51 }
52 ];
53
54 /**
55 * Appears only if they are a part of the required plugins for template site.
56 */
57 const pluginCollection = [
58 {
59 id: 'visualizer',
60 pluginSlug: 'visualizer',
61 label: __('Tables and Chart', 'templates-patterns-collection'),
62 description: __('A powerful and easy to use plugin for responsive charts & tables.', 'templates-patterns-collection')
63 },
64 {
65 id: 'woocommerce',
66 pluginSlug: 'woocommerce',
67 label: __('WooCommerce', 'templates-patterns-collection'),
68 description: __('Build any commerce solution you can imagine.', 'templates-patterns-collection')
69 },
70 {
71 id: 'easy-digital-downloads',
72 pluginSlug: 'easy-digital-downloads',
73 label: __('Easy Digital Downloads', 'templates-patterns-collection'),
74 description: __('Sell digital products with ease and manage your online store efficiently.', 'templates-patterns-collection')
75 },
76 {
77 id: 'edd-blocks',
78 pluginSlug: 'edd-blocks',
79 label: __('EDD Blocks', 'templates-patterns-collection'),
80 description: __('Easily display Easy Digital Downloads products in Gutenberg Editor.', 'templates-patterns-collection')
81 },
82 {
83 id: 'recipe-card-blocks-by-wpzoom',
84 pluginSlug: 'recipe-card-blocks-by-wpzoom',
85 label: __('Recipe Card Blocks', 'templates-patterns-collection'),
86 description: __('Easily create and share mouthwatering recipes.', 'templates-patterns-collection')
87 },
88 {
89 id: 'ameliabooking',
90 pluginSlug: 'ameliabooking',
91 label: __('Amelia', 'templates-patterns-collection'),
92 description: __('Booking system for appointments and event booking.', 'templates-patterns-collection')
93 },
94 {
95 id: 'estatik',
96 pluginSlug: 'estatik',
97 label: __('Estatik', 'templates-patterns-collection'),
98 description: __('Full-featured WordPress real estate plugin.', 'templates-patterns-collection')
99 },
100 {
101 id: 'wp-job-openings',
102 pluginSlug: 'wp-job-openings',
103 label: __('WP Job Openings', 'templates-patterns-collection'),
104 description: __('Plugin for setting up a job listing page for your WordPress website.', 'templates-patterns-collection')
105 },
106 {
107 id: 'pods',
108 pluginSlug: 'pods',
109 label: __('Pods', 'templates-patterns-collection'),
110 description: __('A framework for creating, managing, and deploying customized content types and fields for any project.', 'templates-patterns-collection')
111 },
112 {
113 id: 'restrict-content',
114 pluginSlug: 'restrict-content',
115 label: __('Membership Plugin', 'templates-patterns-collection'),
116 description: __('Membership plugin that allows you to monetize content access.', 'templates-patterns-collection')
117 },
118 {
119 id: 'learning-management-system',
120 pluginSlug: 'learning-management-system',
121 label: __('Masteriyo', 'templates-patterns-collection'),
122 description: __('Create and sell online courses with ease.', 'templates-patterns-collection')
123 }
124 ];
125
126 const FeaturesList = ({ requiredPlugins, onToggle }) => {
127 const [featureList, setFeatureList] = useState( featuredPluginCollection );
128
129 const [selectedFeatures, setSelectedFeatures] = useState({
130 pageBuilder: false,
131 contactForm: false,
132 eCommerce: false,
133 donations: false,
134 automation: false,
135 salesFunnels: false,
136 videoPlayer: false,
137 liveChat: false,
138 });
139
140 const [lockedPluginSlugs, setLockedPluginSlugs] = useState([]);
141
142 const toggleFeature = (feature, pluginSlug) => {
143 if (lockedPluginSlugs.includes(pluginSlug)) {
144 return;
145 }
146
147 const newStatus = !selectedFeatures[feature];
148 if (
149 newStatus || (
150 // Do not disable the plugin installation if another feature that requires it is active.
151 false === newStatus &&
152 featuredPluginCollection.filter( i => pluginSlug === i.pluginSlug && feature !== i.id ).map(({ id }) => selectedFeatures[id]).every(i => false === i )
153 )
154 ) {
155 onToggle(pluginSlug, newStatus);
156 }
157 setSelectedFeatures((prev) => ({
158 ...prev,
159 [feature]: !prev[feature],
160 }));
161 };
162
163 useEffect(() => {
164 const requiredPluginSlugs = Object.keys(requiredPlugins ?? {});
165
166 const compatibleFeaturedPlugins = featuredPluginCollection.filter(feature =>
167 window.tiobDash?.onboardingPluginCompatibility?.[feature.pluginSlug] !== false
168 );
169
170 const allProductDisplay = [...compatibleFeaturedPlugins, ...pluginCollection];
171
172 const missingRequiredPlugins = Object.entries(requiredPlugins ?? {})
173 .filter(([slug]) => allProductDisplay.every(({ pluginSlug }) => slug !== pluginSlug))
174 .map(([slug, label]) => {
175 const decodedLabel = decodeHtmlEntities(label);
176 return {
177 id: slug,
178 pluginSlug: slug,
179 label: decodedLabel || '',
180 description: ''
181 };
182 });
183
184 const requiredProducts = allProductDisplay.filter(({ pluginSlug }) =>
185 requiredPluginSlugs.includes(pluginSlug)
186 );
187
188 const orderedFeatures = [
189 ...requiredProducts,
190 ...missingRequiredPlugins
191 ];
192
193 if (orderedFeatures.length < MAX_FEATURE_LIST_LENGTH) {
194 const additionalFeatures = compatibleFeaturedPlugins.filter(
195 ({ pluginSlug }) => !orderedFeatures.some(f => f.pluginSlug === pluginSlug)
196 );
197
198 const remainingSlots = Math.max(0, MAX_FEATURE_LIST_LENGTH - orderedFeatures.length);
199 if (remainingSlots > 0 && additionalFeatures.length > 0) {
200 orderedFeatures.push(...additionalFeatures.slice(0, remainingSlots));
201 }
202 }
203
204 setFeatureList(orderedFeatures);
205 setLockedPluginSlugs(requiredPluginSlugs);
206 }, [requiredPlugins]);
207
208 return (
209 <div className="ob-select-features">
210 <div className="ob-features-grid">
211 {
212 featureList.map((feature) => {
213 const checked = selectedFeatures[feature.id] || lockedPluginSlugs.includes(feature.pluginSlug);
214 const isLocked = lockedPluginSlugs.includes(feature.pluginSlug);
215 return (
216 <button
217 key={feature.id}
218 className={`ob-feature-card ${
219 checked ? 'selected' : ''
220 } ${isLocked ? 'ob-disabled' : ''}`}
221 onClick={() => toggleFeature(feature.id, feature.pluginSlug)}
222 onKeyPress={(e) => {
223 if (e.key === 'Enter' || e.key === ' ') {
224 toggleFeature(feature.id, feature.pluginSlug);
225 }
226 }}
227 role="checkbox"
228 aria-checked={checked}
229 disabled={isLocked}
230 >
231 <div className="ob-feature-header" data-plugin={feature.pluginSlug}>
232 <h4 className="ob-feature-title">{feature.label}</h4>
233 <input
234 type="checkbox"
235 checked={checked}
236 readOnly
237 />
238 </div>
239 <div className="ob-feature-description">{feature.description}</div>
240 </button>
241 );
242 })
243 }
244 </div>
245 </div>
246 );
247 };
248
249 export default FeaturesList;