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