PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.0
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.0
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 / Complete.vue

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

285 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div class="wpfnl-wz-success" role="main">
3 <!-- Sits behind the content; purely decorative -->
4 <canvas ref="confetti" class="wpfnl-wz-success__confetti" aria-hidden="true"></canvas>
5
6 <div class="wpfnl-wz-success__body">
7 <div class="wpfnl-wz-success__emoji" aria-hidden="true">🎉</div>
8
9 <h1 class="wpfnl-wz-success__heading">Congratulations, You Did it!</h1>
10 <p class="wpfnl-wz-success__sub">
11 WPFunnels is set up on your website! You're now ready to build
12 high-converting sales funnels.
13 </p>
14
15 <div class="wpfnl-wz-success__actions">
16 <button
17 type="button"
18 class="wpfnl-wz-btn wpfnl-wz-btn--lg wpfnl-wz-btn--pill"
19 :class="hasFunnel ? 'wpfnl-wz-btn--ghost' : 'wpfnl-wz-btn--primary'"
20 @click="goToDashboard"
21 >
22 Explore dashboard
23 </button>
24
25 <button
26 v-if="hasFunnel"
27 type="button"
28 class="wpfnl-wz-btn wpfnl-wz-btn--primary wpfnl-wz-btn--lg wpfnl-wz-btn--pill"
29 @click="viewFunnel"
30 >
31 Customize your checkout
32 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">>
33 <path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
34 </svg>
35 </button>
36 </div>
37
38 <div class="wpfnl-wz-success__resources">
39 <p class="wpfnl-wz-success__resources-title">Want to get more out of WPFunnels?</p>
40
41 <div class="wpfnl-wz-success__resources-grid">
42 <a
43 v-for="item in resources"
44 :key="item.label"
45 :href="item.url"
46 target="_blank"
47 rel="noreferrer noopener"
48 class="wpfnl-wz-success__resource-card"
49 >
50 <span class="wpfnl-wz-success__resource-icon" aria-hidden="true" v-html="item.icon"></span>
51 <span class="wpfnl-wz-success__resource-label">{{ item.label }}</span>
52 </a>
53 </div>
54
55 <div class="wpfnl-wz-success__resources-footer">
56 <p>These are always available from your dashboard.</p>
57 <p>
58 Want more power?
59 <a
60 href="https://getwpfunnels.com/pricing/"
61 target="_blank"
62 rel="noreferrer noopener"
63 class="wpfnl-wz-success__pro-link"
64 >See what Pro unlocks</a>
65 </p>
66 </div>
67 </div>
68 </div>
69 </div>
70 </template>
71
72 <script>
73 import apiFetch from '@wordpress/api-fetch';
74
75 /**
76 * Canvas confetti for the completion screen.
77 *
78 * Returns handles so the caller can fade it out and then stop the loop —
79 * leaving a rAF running after the component unmounts would leak.
80 *
81 * @param {HTMLCanvasElement} canvas Target canvas.
82 * @return {{ startFade: Function, stop: Function }} Animation controls.
83 */
84 function runConfetti( canvas ) {
85 const ctx = canvas.getContext( '2d' );
86 const COLORS = [ '#6E42D3', '#a78bfa', '#fbbf24', '#34d399', '#f87171', '#60a5fa', '#f472b6' ];
87
88 let globalAlpha = 1;
89 let fading = false;
90 let animId;
91
92 const pieces = Array.from( { length: 150 }, () => ( {
93 x: Math.random() * canvas.width,
94 y: Math.random() * canvas.height - canvas.height,
95 w: Math.random() * 10 + 5,
96 h: Math.random() * 5 + 3,
97 color: COLORS[ Math.floor( Math.random() * COLORS.length ) ],
98 speed: Math.random() * 3 + 1.5,
99 angle: Math.random() * Math.PI * 2,
100 spin: ( Math.random() - 0.5 ) * 0.1,
101 drift: ( Math.random() - 0.5 ) * 1.5,
102 } ) );
103
104 const draw = () => {
105 ctx.clearRect( 0, 0, canvas.width, canvas.height );
106
107 if ( fading ) {
108 globalAlpha = Math.max( 0, globalAlpha - 0.018 );
109 }
110
111 if ( globalAlpha <= 0 ) {
112 return;
113 }
114
115 pieces.forEach( ( p ) => {
116 ctx.save();
117 ctx.globalAlpha = globalAlpha;
118 ctx.translate( p.x + p.w / 2, p.y + p.h / 2 );
119 ctx.rotate( p.angle );
120 ctx.fillStyle = p.color;
121 ctx.fillRect( -p.w / 2, -p.h / 2, p.w, p.h );
122 ctx.restore();
123
124 p.y += p.speed;
125 p.x += p.drift;
126 p.angle += p.spin;
127
128 if ( p.y > canvas.height + p.h ) {
129 p.y = -p.h;
130 p.x = Math.random() * canvas.width;
131 }
132 } );
133
134 animId = requestAnimationFrame( draw );
135 };
136
137 draw();
138
139 return {
140 startFade: () => {
141 fading = true;
142 },
143 stop: () => cancelAnimationFrame( animId ),
144 };
145 }
146
147 export default {
148 name: 'Complete',
149 props: {
150 funnelId: {
151 type: [ Number, String ],
152 default: null,
153 },
154 firstStepLink: {
155 type: String,
156 default: '',
157 },
158 },
159 emits: [ 'nav' ],
160 data() {
161 return {
162 fadeTimer: null,
163 confetti: null,
164 resources: [
165 {
166 label: 'Blog & tutorials',
167 url: 'https://www.youtube.com/@WPFunnels',
168 icon: '<svg width="28" height="28" viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="8" fill="#fee2e2"/><path d="M11 10l7 4-7 4V10z" fill="#ef4444"/></svg>',
169 },
170 {
171 label: 'Join the community',
172 url: 'https://www.facebook.com/groups/wpfunnels',
173 icon: '<svg width="28" height="28" viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="8" fill="#dbeafe"/><circle cx="12" cy="12" r="3" stroke="#3b82f6" stroke-width="1.4"/><circle cx="18" cy="12" r="3" stroke="#3b82f6" stroke-width="1.4"/><path d="M7 22c0-2.8 2-4.5 5-4.5M21 22c0-2.8-2-4.5-5-4.5" stroke="#3b82f6" stroke-width="1.4" stroke-linecap="round"/></svg>',
174 },
175 {
176 label: 'Get support',
177 url: 'https://wordpress.org/support/plugin/wpfunnels/',
178 icon: '<svg width="28" height="28" viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="8" fill="#d1fae5"/><path d="M10 14v-2a4 4 0 018 0v2" stroke="#059669" stroke-width="1.4" stroke-linecap="round"/><rect x="8" y="14" width="3" height="4" rx="1.5" fill="#059669"/><rect x="17" y="14" width="3" height="4" rx="1.5" fill="#059669"/></svg>',
179 },
180 {
181 label: 'Docs',
182 url: 'https://getwpfunnels.com/docs/getting-started-with-wpfunnels/',
183 icon: '<svg width="28" height="28" viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="8" fill="#e0f2fe"/><rect x="8" y="7" width="12" height="14" rx="2" stroke="#0284c7" stroke-width="1.4"/><path d="M11 12h6M11 15.5h4" stroke="#0284c7" stroke-width="1.4" stroke-linecap="round"/></svg>',
184 },
185 ],
186 };
187 },
188 computed: {
189 /**
190 * Whether the template step actually built something. False when the
191 * user skipped it, in which case there is no funnel to open and the
192 * editor CTA is hidden.
193 */
194 hasFunnel() {
195 return !! this.funnelId;
196 },
197 },
198 mounted() {
199 // The completion screen is terminal — the footer offers no way onward.
200 this.$emit( 'nav', {
201 showBack: false,
202 showNext: false,
203 showSkip: false,
204 canProceed: false,
205 busy: false,
206 label: '',
207 } );
208
209 this.startConfetti();
210 },
211 beforeUnmount() {
212 this.stopConfetti();
213 },
214 methods: {
215 startConfetti() {
216 const canvas = this.$refs.confetti;
217 if ( ! canvas ) {
218 return;
219 }
220
221 canvas.width = window.innerWidth;
222 canvas.height = window.innerHeight;
223
224 this.confetti = runConfetti( canvas );
225 this.fadeTimer = setTimeout( this.confetti.startFade, 3200 );
226 },
227
228 stopConfetti() {
229 clearTimeout( this.fadeTimer );
230 this.confetti?.stop();
231 },
232
233 /**
234 * Report the outcome before leaving the wizard.
235 *
236 * @param {string} action Which exit the user took.
237 * @return {Promise} Always resolves.
238 */
239 completeStep( action ) {
240 const wizardObj = window.setup_wizard_obj || {};
241 const restApiUrl = wizardObj.rest_api_url || '';
242
243 if ( ! restApiUrl ) {
244 return Promise.resolve();
245 }
246
247 const base = restApiUrl.endsWith( '/' ) ? restApiUrl : `${ restApiUrl }/`;
248
249 return apiFetch( {
250 url: `${ base }wpfunnels/v1/setup-wizard/complete-step`,
251 method: 'POST',
252 data: {
253 funnelId: this.funnelId,
254 action,
255 goal: 'improve-checkout',
256 total_steps: 4,
257 },
258 } ).catch( () => {} );
259 },
260
261 async viewFunnel() {
262 await this.completeStep( 'viewFunnel' );
263 const wizardObj = window.setup_wizard_obj || {};
264 const adminUrl = wizardObj.admin_url || '';
265
266 if ( this.funnelId ) {
267 window.location.href = `${ adminUrl }admin.php?page=edit_funnel&id=${ this.funnelId }`;
268 return;
269 }
270 if ( this.firstStepLink ) {
271 window.open( this.firstStepLink, '_blank' );
272 return;
273 }
274 window.location.href = wizardObj.dashboard_url || `${ adminUrl }admin.php?page=wpfunnels`;
275 },
276
277 async goToDashboard() {
278 await this.completeStep( 'goToDashboard' );
279 const wizardObj = window.setup_wizard_obj || {};
280 window.location.href = wizardObj.dashboard_url || `${ wizardObj.admin_url }admin.php?page=wpfunnels`;
281 },
282 },
283 };
284 </script>
285