PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.4
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.4
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / admin / modules / setup-wizard / components / Content / Setup.vue

Setup.vue in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.12.4, at admin/modules/setup-wizard/components/Content/Setup.vue

416 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div class="wpfnl-required-installation">
3
4 <!-- Header — matches .wpfnl-mm-setup-header pattern from other steps -->
5 <div class="wpfnl-mm-setup-header">
6 <h2 class="wpfnl-mm-setup-title">{{ pluginsSectionTitle }}</h2>
7 <p class="wpfnl-mm-setup-subtitle">{{ pluginsSectionSubtitle }}</p>
8 </div>
9
10 <!-- Single unified white card — matches .wpfnl-mm-setup-section pattern -->
11 <div class="wpfnl-ri-card">
12
13 <!-- Builder Selection -->
14 <div class="wpfnl-ri-builder-section">
15 <p class="wpfnl-ri-builder-label">What page builder are you using?</p>
16 <div class="wpfnl-ri-builder-options">
17 <button
18 v-for="builder in builders"
19 :key="builder.id"
20 class="wpfnl-ri-builder-btn"
21 :class="{
22 'active': selectedBuilder === builder.id,
23 'disabled': isProBuilder(builder.id) && !isBuilderInstalled(builder.id)
24 }"
25 @click="selectBuilder(builder.id)"
26 :disabled="isProBuilder(builder.id) && !isBuilderInstalled(builder.id)"
27 >
28 <img :src="builder.image" :alt="builder.name" />
29 <span>{{ builder.name }}</span>
30 <span class="wpfnl-ri-not-installed" v-if="isProBuilder(builder.id) && !isBuilderInstalled(builder.id)">Not Installed</span>
31 </button>
32 </div>
33 </div>
34
35 <!-- Divider -->
36 <div class="wpfnl-ri-divider"></div>
37
38 <!-- Plugins Section -->
39 <div class="wpfnl-ri-plugins-section">
40 <div class="wpfnl-ri-plugin-cards">
41
42 <!-- WooCommerce — required -->
43 <div class="wpfnl-ri-plugin-card" :class="pluginCardClass('woocommerce')">
44 <div class="wpfnl-ri-plugin-card-icon">
45 <img :src="wooLogo" alt="WooCommerce" />
46 </div>
47 <div class="wpfnl-ri-plugin-card-body">
48 <div class="wpfnl-ri-plugin-card-header">
49 <span class="wpfnl-ri-plugin-card-name">WooCommerce</span>
50 <span class="wpfnl-ri-plugin-card-required">Required</span>
51 </div>
52 <p class="wpfnl-ri-plugin-card-desc">Process payments and manage orders for your funnels.</p>
53 </div>
54 <div class="wpfnl-ri-plugin-card-status">
55 <span v-if="isPluginActive('woocommerce')" class="wpfnl-ri-status wpfnl-ri-status--active">
56 <svg width="10" height="10" viewBox="0 0 10 10" fill="none">><path d="M8.5 2.5L4 7L1.5 4.5" stroke="#059669" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
57 Active
58 </span>
59 <span v-else-if="isPluginInstalled('woocommerce')" class="wpfnl-ri-status wpfnl-ri-status--installed">
60 <svg width="8" height="8" viewBox="0 0 8 8">><circle cx="4" cy="4" r="4" fill="#F59E0B"/></svg>
61 Installed
62 </span>
63 <span v-else class="wpfnl-ri-status wpfnl-ri-status--missing">
64 <svg width="10" height="10" viewBox="0 0 10 10" fill="none">><path d="M5 1v4M5 7.5v.5" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round"/></svg>
65 Not Installed
66 </span>
67 </div>
68 </div>
69
70 <!-- Mail Mint — optional, toggleable. Hidden when already active. -->
71 <div
72 v-if="!isPluginActive('mail-mint')"
73 class="wpfnl-ri-plugin-card wpfnl-ri-plugin-card--optional"
74 :class="mailMintCardClass"
75 @click="toggleMailMint"
76 role="checkbox"
77 :aria-checked="mailMintSelected"
78 tabindex="0"
79 @keydown.space.prevent="toggleMailMint"
80 @keydown.enter.prevent="toggleMailMint"
81 >
82 <div class="wpfnl-ri-plugin-card-icon">
83 <img :src="mailMintLogo" alt="Mail Mint" />
84 </div>
85 <div class="wpfnl-ri-plugin-card-body">
86 <div class="wpfnl-ri-plugin-card-header">
87 <span class="wpfnl-ri-plugin-card-name">Mail Mint</span>
88 <span class="wpfnl-ri-plugin-card-optional">Optional</span>
89 </div>
90 <p class="wpfnl-ri-plugin-card-desc">Email marketing and abandoned cart recovery for your store.</p>
91 </div>
92 <div class="wpfnl-ri-plugin-card-status">
93 <span v-if="mailMintSelected && isPluginActive('mail-mint')" class="wpfnl-ri-status wpfnl-ri-status--active">
94 <svg width="10" height="10" viewBox="0 0 10 10" fill="none">><path d="M8.5 2.5L4 7L1.5 4.5" stroke="#059669" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
95 Active
96 </span>
97 <span v-else-if="mailMintSelected && isPluginInstalled('mail-mint')" class="wpfnl-ri-status wpfnl-ri-status--installed">
98 <svg width="8" height="8" viewBox="0 0 8 8">><circle cx="4" cy="4" r="4" fill="#F59E0B"/></svg>
99 Installed
100 </span>
101 <span v-else-if="mailMintSelected" class="wpfnl-ri-status wpfnl-ri-status--missing">
102 <svg width="10" height="10" viewBox="0 0 10 10" fill="none">><path d="M5 1v4M5 7.5v.5" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round"/></svg>
103 Not Installed
104 </span>
105 </div>
106 <div class="wpfnl-ri-plugin-card-toggle">
107 <div class="mint-toggle" :class="{ 'mint-toggle--on': mailMintSelected }">
108 <div class="mint-toggle__knob"></div>
109 </div>
110 </div>
111 </div>
112
113 </div>
114 </div>
115
116 </div>
117
118 <!-- Footer Buttons -->
119 <div class="wpfnl-mm-choose-goal-buttons wpfnl-mm-buttons-container">
120 <button class="wpfnl-mm-btn wpfnl-mm-btn-secondary" @click="goBack" :disabled="isProcessing">
121 <svg width="17" height="12" viewBox="0 0 17 12" fill="none" xmlns="http://www.w3.org/2000/svg">>
122 <path d="M16 6H1M1 6L6 1M1 6L6 11" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
123 </svg>
124 Back
125 </button>
126 <div class="wpfnl-mm-btn-group">
127 <button
128 class="wpfnl-mm-btn wpfnl-mm-btn-primary"
129 @click="handleContinue"
130 :disabled="isProcessing"
131 >
132 {{ installButtonLabel }}
133 <svg v-if="!isProcessing" width="17" height="12" viewBox="0 0 17 12" fill="none" xmlns="http://www.w3.org/2000/svg">>
134 <path d="M1 6H16M16 6L11 1M16 6L11 11" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
135 </svg>
136 </button>
137 </div>
138 </div>
139
140 </div>
141 </template>
142
143 <script>
144 export default {
145 name: 'Setup',
146 data() {
147 return {
148 selectedBuilder: 'gutenberg',
149 builderStatuses: {},
150 pluginStatuses: {},
151 isProcessing: false,
152 installStatusText: '',
153 mailMintSelected: false,
154 builders: [
155 { id: 'gutenberg', name: 'Gutenberg', image: window?.setup_wizard_obj?.gb_builder_img || '', isBuiltIn: true },
156 { id: 'elementor', name: 'Elementor', image: window?.setup_wizard_obj?.elementor_img || '' },
157 { id: 'bricks', name: 'Bricks', image: window?.setup_wizard_obj?.bricks_img || '', isTheme: true },
158 { id: 'divi', name: 'Divi', image: window?.setup_wizard_obj?.divi_img || '', isTheme: true },
159 { id: 'oxygen', name: 'Oxygen', image: window?.setup_wizard_obj?.oxygen_img || '' },
160 { id: 'others', name: 'Others', image: window?.setup_wizard_obj?.others_builder_img || '', isBuiltIn: true },
161 ],
162 proBuilders: ['oxygen', 'bricks', 'divi'],
163 plugins: [
164 { id: 'woocommerce', name: 'WooCommerce', slug: 'woocommerce', basename: 'woocommerce/woocommerce.php', installedKey: 'is_woo_installed', activeKey: 'is_woo_active' },
165 { id: 'mail-mint', name: 'Mail Mint', slug: 'mail-mint', basename: 'mail-mint/mail-mint.php', installedKey: 'is_mrm_installed', activeKey: 'is_mrm_active' },
166 ],
167 }
168 },
169 computed: {
170 wooLogo() {
171 return window?.setup_wizard_obj?.wc_logo || '';
172 },
173 mailMintLogo() {
174 return window?.setup_wizard_obj?.mail_mint_logo || '';
175 },
176
177 wooStatus() {
178 const s = this.pluginStatuses['woocommerce'] || {};
179 if (s.active) return 'active';
180 if (s.installed) return 'installed';
181 return 'missing';
182 },
183
184 pluginsSectionTitle() {
185 if (this.wooStatus === 'active') {
186 return 'Get essential plugins installed with one-click';
187 }
188 if (this.wooStatus === 'installed') {
189 return 'WooCommerce is installed but not active';
190 }
191 return 'WooCommerce is required to use WPFunnels';
192 },
193
194 pluginsSectionSubtitle() {
195 const mintAlreadyActive = this.isPluginActive('mail-mint');
196 const mintPart = !mintAlreadyActive
197 ? ' You can also add Mail Mint for email marketing and abandoned cart recovery.'
198 : '';
199 if (this.wooStatus === 'active') {
200 return mintAlreadyActive
201 ? 'WooCommerce and Mail Mint are already active. You\'re all set!'
202 : 'WooCommerce is already active. Optionally install Mail Mint for email marketing and abandoned cart recovery.';
203 }
204 if (this.wooStatus === 'installed') {
205 return 'We will activate WooCommerce to process payments.' + mintPart;
206 }
207 return 'WPFunnels uses WooCommerce to process payments. We will install and activate it for you.' + mintPart;
208 },
209
210 installButtonLabel() {
211 if (this.isProcessing) {
212 return this.installStatusText || 'Installing and Activating...';
213 }
214 const wooActive = this.isPluginActive('woocommerce');
215 const wooInstalled = this.isPluginInstalled('woocommerce');
216 // If Mail Mint is already active, treat it as not needing action
217 const mintAlreadyActive = this.isPluginActive('mail-mint');
218 const mintActive = mintAlreadyActive || !this.mailMintSelected || this.isPluginActive('mail-mint');
219 const mintInstalled = mintAlreadyActive || !this.mailMintSelected || this.isPluginInstalled('mail-mint');
220
221 if (wooActive && mintActive) return 'Continue';
222
223 const needsInstall = (!wooInstalled && !wooActive)
224 || (!mintAlreadyActive && this.mailMintSelected && !mintInstalled && !mintActive);
225
226 return needsInstall ? 'Install and Activate' : 'Activate';
227 },
228
229 // Mail Mint card class lives in computed so Vue tracks mailMintSelected reactively
230 mailMintCardClass() {
231 if (!this.mailMintSelected) return 'wpfnl-ri-plugin-card--unchecked';
232 if (this.isPluginActive('mail-mint')) return 'wpfnl-ri-plugin-card--active';
233 if (this.isPluginInstalled('mail-mint')) return 'wpfnl-ri-plugin-card--installed';
234 return '';
235 },
236 },
237 mounted() {
238 this.detectBuilder();
239 this.checkPluginStatuses();
240 },
241 methods: {
242 detectBuilder() {
243 const getPlugins = window?.setup_wizard_obj?.getPlugins || [];
244 const oxygenData = getPlugins.find(p => p.name === 'oxygen');
245 const bricksData = getPlugins.find(p => p.name === 'bricks');
246 const diviData = getPlugins.find(p => p.name === 'divi');
247
248 const checks = [
249 { id: 'elementor', active: window?.setup_wizard_obj?.is_elementor_active === 'yes', installed: window?.setup_wizard_obj?.is_elementor_installed === 'yes' },
250 { id: 'bricks', active: bricksData?.status === 'activated', installed: bricksData?.status === 'activated' || bricksData?.status === 'installed' },
251 { id: 'divi', active: diviData?.status === 'activated', installed: diviData?.status === 'activated' || diviData?.status === 'installed' },
252 { id: 'oxygen', active: oxygenData?.status === 'activated', installed: oxygenData?.status === 'activated' || oxygenData?.status === 'installed' },
253 ];
254
255 const statuses = { gutenberg: { installed: true, active: true }, others: { installed: true, active: true } };
256 checks.forEach(b => { statuses[b.id] = { installed: b.installed, active: b.active }; });
257 this.builderStatuses = statuses;
258
259 // 1. Use previously saved builder setting if it exists
260 const savedBuilder = window?.setup_wizard_obj?.defaultSettings?.builder;
261 if (savedBuilder && savedBuilder !== 'gutenberg') {
262 // Validate the saved builder is actually installed/active before trusting it
263 const savedStatus = statuses[savedBuilder];
264 if (savedStatus?.installed || savedStatus?.active) {
265 this.selectedBuilder = savedBuilder;
266 return;
267 }
268 }
269
270 // 2. If saved is gutenberg (or no valid saved setting), default to gutenberg
271 this.selectedBuilder = 'gutenberg';
272 },
273
274 checkPluginStatuses() {
275 const statuses = {};
276 this.plugins.forEach(p => {
277 statuses[p.id] = {
278 installed: p.installedKey ? window?.setup_wizard_obj?.[p.installedKey] === 'yes' : false,
279 active: p.activeKey ? window?.setup_wizard_obj?.[p.activeKey] === 'yes' : false,
280 };
281 });
282 this.pluginStatuses = statuses;
283 },
284
285 isProBuilder(id) { return this.proBuilders.includes(id); },
286 isBuilderInstalled(id) { return !!this.builderStatuses[id]?.installed; },
287 isPluginActive(id) { return !!this.pluginStatuses[id]?.active; },
288 isPluginInstalled(id) { return !!this.pluginStatuses[id]?.installed; },
289
290 pluginCardClass(id) {
291 if (this.isPluginActive(id)) return 'wpfnl-ri-plugin-card--active';
292 if (this.isPluginInstalled(id)) return 'wpfnl-ri-plugin-card--installed';
293 return '';
294 },
295
296 toggleMailMint() {
297 this.mailMintSelected = !this.mailMintSelected;
298 },
299
300 selectBuilder(id) {
301 if (this.isProBuilder(id) && !this.isBuilderInstalled(id)) return;
302 this.selectedBuilder = id;
303 },
304
305 async installPlugin(slug) {
306 return new Promise(resolve => {
307 wp.updates.ajax('install-plugin', { slug, success: () => resolve(true), error: () => resolve(false) });
308 });
309 },
310
311 async activatePlugin(basename) {
312 return new Promise(resolve => {
313 const ajaxUrl = window.ajaxurl || window.location.origin + '/wp-admin/admin-ajax.php';
314 const action = basename === 'mail-mint/mail-mint.php' ? 'wpfnl_activate_mail_mint' : 'wpfnl_activate_plugin';
315 const data = { action };
316 if (action === 'wpfnl_activate_plugin') data.plugin = basename;
317 jQuery.ajax({ url: ajaxUrl, type: 'POST', data, success: r => resolve(!!r.success), error: () => resolve(false) });
318 });
319 },
320
321 async installAndActivatePlugin(slug, basename, isInstalled = false) {
322 try {
323 if (!isInstalled) {
324 this.installStatusText = 'Installing and Activating...';
325 const ok = await this.installPlugin(slug);
326 if (!ok) return false;
327 await new Promise(r => setTimeout(r, 1500));
328 }
329 this.installStatusText = 'Installing and Activating...';
330 return await this.activatePlugin(basename);
331 } catch { return false; }
332 },
333
334 async installBuilder() {
335 if (this.selectedBuilder === 'gutenberg' || this.selectedBuilder === 'others') return true;
336 const map = {
337 elementor: { slug: 'elementor', basename: 'elementor/elementor.php', installedKey: 'is_elementor_installed', activeKey: 'is_elementor_active' },
338 divi: { slug: 'divi-builder', basename: 'divi-builder/divi-builder.php', installedKey: null, activeKey: null },
339 oxygen: { slug: 'oxygen', basename: 'oxygen/functions.php', installedKey: null, activeKey: null },
340 bricks: { slug: 'bricks', basename: 'bricks', isTheme: true },
341 };
342 const b = map[this.selectedBuilder];
343 if (!b || b.isTheme) return true;
344 const isInstalled = b.installedKey ? window?.setup_wizard_obj?.[b.installedKey] === 'yes' : false;
345 const isActive = b.activeKey ? window?.setup_wizard_obj?.[b.activeKey] === 'yes' : false;
346 if (isInstalled && isActive) return true;
347 return this.installAndActivatePlugin(b.slug, b.basename, isInstalled);
348 },
349
350 async installPlugins() {
351 for (const plugin of this.plugins) {
352 if (plugin.id === 'mail-mint' && (!this.mailMintSelected || this.isPluginActive('mail-mint'))) continue;
353 const isInstalled = plugin.installedKey ? window?.setup_wizard_obj?.[plugin.installedKey] === 'yes' : false;
354 const isActive = plugin.activeKey ? window?.setup_wizard_obj?.[plugin.activeKey] === 'yes' : false;
355 if (isInstalled && isActive) continue;
356 await this.installAndActivatePlugin(plugin.slug, plugin.basename, isInstalled);
357 }
358 },
359
360 async handleContinue() {
361 this.isProcessing = true;
362 this.installStatusText = '';
363 try {
364 await this.installBuilder();
365 await this.installPlugins();
366 const builder = this.selectedBuilder === 'others' ? 'gutenberg' : this.selectedBuilder;
367 this.$emit('next-step', { builder });
368 } catch {
369 alert('An error occurred during setup. Please try again.');
370 } finally {
371 this.isProcessing = false;
372 this.installStatusText = '';
373 }
374 },
375
376 goBack() { this.$emit('prev-step'); },
377 }
378 }
379 </script>
380
381 <style scoped>
382 /* Toggle switch — scoped to this component, no specificity conflicts */
383 .mint-toggle {
384 position: relative;
385 display: inline-block;
386 width: 36px;
387 height: 20px;
388 border-radius: 100px;
389 background: #D1D5DB;
390 transition: background 0.25s ease;
391 cursor: pointer;
392 flex-shrink: 0;
393 }
394
395 .mint-toggle--on {
396 background: #6E42D3;
397 }
398
399 .mint-toggle__knob {
400 position: absolute;
401 top: 2px;
402 left: 2px;
403 width: 16px;
404 height: 16px;
405 border-radius: 50%;
406 background: #FFF;
407 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
408 transition: transform 0.25s ease;
409 transform: translateX(0);
410 }
411
412 .mint-toggle--on .mint-toggle__knob {
413 transform: translateX(16px);
414 }
415 </style>
416