PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / booking-form / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/booking-form/frontend.js

476 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
5 var DAYS_SHORT = ['Su','Mo','Tu','We','Th','Fr','Sa'];
6 var DAYS_LONG = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
7
8 function typoCssVarsForEl(typo, prefix, el) {
9 if (!typo) return;
10 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
11 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
12 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
13 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
14 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
15 var su = typo.sizeUnit || 'px';
16 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
17 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
18 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
19 var lhu = typo.lineHeightUnit || '';
20 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
21 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
22 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
23 var lsu = typo.letterSpacingUnit || 'px';
24 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
25 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
26 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
27 var wsu = typo.wordSpacingUnit || 'px';
28 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
29 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
30 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
31 }
32
33 document.querySelectorAll('.bkbg-bf-app').forEach(function (app) {
34 var opts;
35 try { opts = JSON.parse(app.dataset.opts || '{}'); } catch (e) { return; }
36
37 var services = opts.services || [];
38 var timeSlots = opts.timeSlots || [];
39 var blockedDays = opts.blockedDays || [0, 6];
40 var showSvcSelect = opts.showServiceSelect !== false;
41 var showName = opts.showNameField !== false;
42 var showEmail = opts.showEmailField !== false;
43 var showPhone = opts.showPhoneField !== false;
44 var showNotes = opts.showNotes !== false;
45 var showPrice = opts.showPrice !== false;
46 var formTitle = opts.formTitle || 'Book an Appointment';
47 var formSubtitle = opts.formSubtitle || 'Choose your preferred date and time.';
48 var submitLabel = opts.submitLabel || 'Confirm Booking';
49 var confTitle = opts.confirmationTitle || 'Booking Confirmed! 🎉';
50 var confMsg = opts.confirmationMsg || 'Thank you! We\'ve received your booking.';
51 var formWidth = parseInt(opts.formWidth) || 600;
52 var formRadius = parseInt(opts.formRadius) || 20;
53 var inputRadius = parseInt(opts.inputRadius) || 10;
54 var calRadius = parseInt(opts.calendarRadius) || 12;
55 var sectionBg = opts.sectionBg || '';
56
57 // CSS vars
58 app.style.setProperty('--bf-accent', opts.accentColor || '#6366f1');
59 app.style.setProperty('--bf-btn-bg', opts.btnBg || '#6366f1');
60 app.style.setProperty('--bf-btn-clr', opts.btnColor || '#ffffff');
61 app.style.setProperty('--bf-form-bg', opts.formBg || '#ffffff');
62 app.style.setProperty('--bf-form-border', opts.formBorder || '#e2e8f0');
63 app.style.setProperty('--bf-cal-bg', opts.calendarBg || '#f8fafc');
64 app.style.setProperty('--bf-cal-header', opts.calendarHeaderBg || '#6366f1');
65 app.style.setProperty('--bf-cal-header-clr', opts.calendarHeaderColor || '#ffffff');
66 app.style.setProperty('--bf-sel-day', opts.selectedDayBg || '#6366f1');
67 app.style.setProperty('--bf-sel-day-clr', opts.selectedDayColor || '#ffffff');
68 app.style.setProperty('--bf-slot-bg', opts.slotBg || '#f1f5f9');
69 app.style.setProperty('--bf-slot-sel', opts.slotSelectedBg || '#6366f1');
70 app.style.setProperty('--bf-slot-sel-clr', opts.slotSelectedColor || '#ffffff');
71 app.style.setProperty('--bf-input-bg', opts.inputBg || '#f8fafc');
72 app.style.setProperty('--bf-input-border', opts.inputBorder || '#e2e8f0');
73 app.style.setProperty('--bf-text', opts.textColor || '#0f172a');
74 app.style.setProperty('--bf-muted', opts.mutedColor || '#64748b');
75 app.style.setProperty('--bf-width', formWidth + 'px');
76 app.style.setProperty('--bf-input-radius', inputRadius + 'px');
77 app.style.setProperty('--bf-cal-radius', calRadius + 'px');
78 if (sectionBg) app.style.background = sectionBg;
79 typoCssVarsForEl(opts.typoTitle, '--bkbg-bfm-title-', app);
80 typoCssVarsForEl(opts.typoSubtitle, '--bkbg-bfm-sub-', app);
81
82 // ── State ──
83 var currentStep = 1; // 1=service+date, 2=details, 3=confirm
84 var selectedService = services[0] || null;
85 var selectedDate = null; // Date object
86 var selectedTime = null; // string e.g. "10:00"
87 var calYear, calMonth;
88 var today = new Date();
89 calYear = today.getFullYear();
90 calMonth = today.getMonth();
91
92 // ── Helpers ──
93 function el(tag, className) {
94 var d = document.createElement(tag);
95 if (className) d.className = className;
96 return d;
97 }
98 function txt(text) { return document.createTextNode(text); }
99
100 function formatDate(d) {
101 if (!d) return '';
102 return DAYS_LONG[d.getDay()] + ', ' + MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear();
103 }
104
105 // ── Build ──
106 var wrap = el('div', 'bkbg-bf-wrap');
107 app.appendChild(wrap);
108
109 var card = el('div', 'bkbg-bf-card');
110 card.style.borderRadius = formRadius + 'px';
111 wrap.appendChild(card);
112
113 // Header
114 var header = el('div', 'bkbg-bf-header');
115 var titleEl = el('h2', 'bkbg-bf-title');
116 titleEl.textContent = formTitle;
117 var subEl = el('p', 'bkbg-bf-subtitle');
118 subEl.textContent = formSubtitle;
119 header.appendChild(titleEl);
120 header.appendChild(subEl);
121 card.appendChild(header);
122
123 // Steps
124 var stepsBar = el('div', 'bkbg-bf-steps');
125 var stepDefs = showSvcSelect
126 ? [{ n: 1, label: 'Service & Date' }, { n: 2, label: 'Your Details' }, { n: 3, label: 'Confirm' }]
127 : [{ n: 1, label: 'Date & Time' }, { n: 2, label: 'Your Details' }, { n: 3, label: 'Confirm' }];
128 var stepEls = [];
129 stepDefs.forEach(function (s) {
130 var stepEl = el('div', 'bkbg-bf-step');
131 var num = el('span', 'bkbg-bf-step-num');
132 num.textContent = s.n;
133 stepEl.appendChild(num);
134 stepEl.appendChild(txt(' ' + s.label));
135 stepsBar.appendChild(stepEl);
136 stepEls.push(stepEl);
137 });
138 card.appendChild(stepsBar);
139
140 // Content
141 var content = el('div', 'bkbg-bf-content');
142 card.appendChild(content);
143
144 // ── Section 1: service + date/time ──
145 var sec1 = el('div', 'bkbg-bf-section bkbg-bf-section-active');
146 content.appendChild(sec1);
147
148 // Service selector
149 var svcGrid = null;
150 if (showSvcSelect && services.length) {
151 var svcTitle = el('p', 'bkbg-bf-slots-label');
152 svcTitle.textContent = 'Select Service';
153 sec1.appendChild(svcTitle);
154 svcGrid = el('div', 'bkbg-bf-service-grid');
155 sec1.appendChild(svcGrid);
156
157 var svcEls = [];
158 services.forEach(function (svc, si) {
159 var opt = el('div', 'bkbg-bf-service-option');
160 opt.style.borderRadius = inputRadius + 'px';
161 var left = el('div');
162 var name = el('div', 'bkbg-bf-svc-name');
163 name.textContent = svc.label;
164 var meta = el('div', 'bkbg-bf-svc-meta');
165 meta.textContent = svc.duration + ' min';
166 left.appendChild(name);
167 left.appendChild(meta);
168 opt.appendChild(left);
169 if (showPrice && svc.price) {
170 var price = el('div', 'bkbg-bf-svc-price');
171 price.textContent = svc.price;
172 opt.appendChild(price);
173 }
174 opt.addEventListener('click', function () {
175 selectedService = svc;
176 svcEls.forEach(function (e){ e.classList.remove('bkbg-bf-selected'); });
177 opt.classList.add('bkbg-bf-selected');
178 });
179 svcGrid.appendChild(opt);
180 svcEls.push(opt);
181 if (si === 0) opt.classList.add('bkbg-bf-selected');
182 });
183 }
184
185 // ── Calendar ──
186 var calTitle = el('p', 'bkbg-bf-slots-label');
187 calTitle.textContent = 'Select Date';
188 sec1.appendChild(calTitle);
189
190 var calDiv = el('div', 'bkbg-bf-calendar');
191 sec1.appendChild(calDiv);
192
193 var calHeader = el('div', 'bkbg-bf-cal-header');
194 var btnPrev = el('button', 'bkbg-bf-cal-nav');
195 btnPrev.type = 'button';
196 btnPrev.textContent = '';
197 var calMonthLabel = el('span', 'bkbg-bf-cal-month');
198 var btnNext = el('button', 'bkbg-bf-cal-nav');
199 btnNext.type = 'button';
200 btnNext.textContent = '';
201 calHeader.appendChild(btnPrev);
202 calHeader.appendChild(calMonthLabel);
203 calHeader.appendChild(btnNext);
204 calDiv.appendChild(calHeader);
205
206 var daysHeader = el('div', 'bkbg-bf-cal-days-header');
207 DAYS_SHORT.forEach(function(d) {
208 var dn = el('div', 'bkbg-bf-cal-day-name');
209 dn.textContent = d;
210 daysHeader.appendChild(dn);
211 });
212 calDiv.appendChild(daysHeader);
213
214 var calGrid = el('div', 'bkbg-bf-cal-grid');
215 calDiv.appendChild(calGrid);
216
217 function renderCal() {
218 calMonthLabel.textContent = MONTHS[calMonth] + ' ' + calYear;
219 calGrid.innerHTML = '';
220 var firstDay = new Date(calYear, calMonth, 1).getDay();
221 var daysInMonth = new Date(calYear, calMonth + 1, 0).getDate();
222 // Padding cells
223 for (var p = 0; p < firstDay; p++) {
224 var empty = el('div', 'bkbg-bf-cal-day bkbg-bf-empty');
225 calGrid.appendChild(empty);
226 }
227 for (var d = 1; d <= daysInMonth; d++) {
228 var date = new Date(calYear, calMonth, d);
229 var dow = date.getDay();
230 var isPast = date < new Date(today.getFullYear(), today.getMonth(), today.getDate());
231 var isBlocked = blockedDays.indexOf(dow) !== -1;
232 var isToday = date.getDate() === today.getDate() && calYear === today.getFullYear() && calMonth === today.getMonth();
233 var isSel = selectedDate && date.toDateString() === selectedDate.toDateString();
234
235 var cls = 'bkbg-bf-cal-day';
236 if (isPast) cls += ' bkbg-bf-past';
237 if (isBlocked) cls += ' bkbg-bf-blocked';
238 if (isToday) cls += ' bkbg-bf-today';
239 if (isSel) cls += ' bkbg-bf-selected';
240
241 var dayEl = el('div', cls);
242 dayEl.textContent = d;
243
244 if (!isPast && !isBlocked) {
245 (function(dt) {
246 dayEl.addEventListener('click', function () {
247 selectedDate = dt;
248 selectedTime = null;
249 renderCal();
250 renderSlots();
251 });
252 })(date);
253 }
254 calGrid.appendChild(dayEl);
255 }
256 }
257
258 btnPrev.addEventListener('click', function () {
259 calMonth--;
260 if (calMonth < 0) { calMonth = 11; calYear--; }
261 renderCal();
262 });
263 btnNext.addEventListener('click', function () {
264 calMonth++;
265 if (calMonth > 11) { calMonth = 0; calYear++; }
266 renderCal();
267 });
268
269 // ── Time Slots ──
270 var slotsLabel = el('p', 'bkbg-bf-slots-label');
271 slotsLabel.textContent = 'Available Times';
272 sec1.appendChild(slotsLabel);
273
274 var slotsGrid = el('div', 'bkbg-bf-slots-grid');
275 sec1.appendChild(slotsGrid);
276
277 function renderSlots() {
278 slotsGrid.innerHTML = '';
279 if (!selectedDate) {
280 var hint = el('p', 'bkbg-bf-no-slots');
281 hint.textContent = 'Please select a date to see available times.';
282 slotsGrid.appendChild(hint);
283 return;
284 }
285 if (!timeSlots.length) {
286 var noSlots = el('p', 'bkbg-bf-no-slots');
287 noSlots.textContent = 'No time slots configured.';
288 slotsGrid.appendChild(noSlots);
289 return;
290 }
291 timeSlots.forEach(function (t) {
292 var slotEl = el('div', 'bkbg-bf-slot' + (t === selectedTime ? ' bkbg-bf-slot-selected' : ''));
293 slotEl.textContent = t;
294 slotEl.addEventListener('click', function () {
295 selectedTime = t;
296 renderSlots();
297 });
298 slotsGrid.appendChild(slotEl);
299 });
300 }
301
302 // Nav buttons sec1
303 var nav1 = el('div', 'bkbg-bf-nav-btns');
304 var btn1Next = el('button', 'bkbg-bf-btn-primary');
305 btn1Next.type = 'button';
306 btn1Next.style.borderRadius = inputRadius + 'px';
307 btn1Next.textContent = 'Continue →';
308 nav1.appendChild(btn1Next);
309 sec1.appendChild(nav1);
310
311 btn1Next.addEventListener('click', function () {
312 if (!selectedDate) { alert('Please select a date.'); return; }
313 if (!selectedTime) { alert('Please select a time slot.'); return; }
314 goToStep(2);
315 });
316
317 // ── Section 2: contact details ──
318 var sec2 = el('div', 'bkbg-bf-section');
319 content.appendChild(sec2);
320
321 function detailInp(type, placeholder, name) {
322 var g = el('div', 'bkbg-bf-group');
323 var lab = el('label', 'bkbg-bf-label');
324 lab.textContent = placeholder;
325 var inp2 = el(type === 'textarea' ? 'textarea' : 'input', type === 'textarea' ? 'bkbg-bf-textarea' : 'bkbg-bf-input');
326 if (type !== 'textarea') inp2.type = type;
327 inp2.name = name;
328 inp2.placeholder = placeholder;
329 inp2.style.borderRadius = inputRadius + 'px';
330 g.appendChild(lab);
331 g.appendChild(inp2);
332 return { group: g, input: inp2 };
333 }
334
335 var nameField = null;
336 var emailField = null;
337 var phoneField = null;
338 var notesField = null;
339
340 if (showName) {
341 nameField = detailInp('text', 'Your Full Name', 'name');
342 sec2.appendChild(nameField.group);
343 }
344 if (showEmail) {
345 emailField = detailInp('email', 'Email Address', 'email');
346 sec2.appendChild(emailField.group);
347 }
348 if (showPhone) {
349 phoneField = detailInp('tel', 'Phone Number', 'phone');
350 sec2.appendChild(phoneField.group);
351 }
352 if (showNotes) {
353 notesField = detailInp('textarea', 'Additional Notes (optional)', 'notes');
354 sec2.appendChild(notesField.group);
355 }
356
357 var nav2 = el('div', 'bkbg-bf-nav-btns');
358 var btn2Back = el('button', 'bkbg-bf-btn-secondary');
359 btn2Back.type = 'button';
360 btn2Back.style.borderRadius = inputRadius + 'px';
361 btn2Back.textContent = '← Back';
362 var btn2Next = el('button', 'bkbg-bf-btn-primary');
363 btn2Next.type = 'button';
364 btn2Next.style.borderRadius = inputRadius + 'px';
365 btn2Next.textContent = 'Review Booking →';
366 nav2.appendChild(btn2Back);
367 nav2.appendChild(btn2Next);
368 sec2.appendChild(nav2);
369
370 btn2Back.addEventListener('click', function () { goToStep(1); });
371 btn2Next.addEventListener('click', function () {
372 if (showEmail && emailField && !emailField.input.value) { alert('Please enter your email.'); return; }
373 goToStep(3);
374 });
375
376 // ── Section 3: summary + submit ──
377 var sec3 = el('div', 'bkbg-bf-section');
378 content.appendChild(sec3);
379
380 var summaryDiv = el('div', 'bkbg-bf-summary');
381 sec3.appendChild(summaryDiv);
382
383 function buildSummary() {
384 summaryDiv.innerHTML = '';
385 function row(label, val) {
386 var r = el('div', 'bkbg-bf-summary-row');
387 var l = el('span', 'bkbg-bf-summary-label');
388 l.textContent = label;
389 var v = el('span');
390 v.textContent = val;
391 r.appendChild(l);
392 r.appendChild(v);
393 summaryDiv.appendChild(r);
394 }
395 if (showSvcSelect && selectedService) row('Service', selectedService.label + (showPrice && selectedService.price ? '' + selectedService.price : ''));
396 row('Date', formatDate(selectedDate));
397 row('Time', selectedTime || '');
398 if (showName && nameField) row('Name', nameField.input.value);
399 if (showEmail && emailField) row('Email', emailField.input.value);
400 if (showPhone && phoneField) row('Phone', phoneField.input.value);
401 }
402
403 var nav3 = el('div', 'bkbg-bf-nav-btns');
404 var btn3Back = el('button', 'bkbg-bf-btn-secondary');
405 btn3Back.type = 'button';
406 btn3Back.style.borderRadius = inputRadius + 'px';
407 btn3Back.textContent = '← Back';
408 var btn3Submit = el('button', 'bkbg-bf-btn-primary');
409 btn3Submit.type = 'button';
410 btn3Submit.style.borderRadius = inputRadius + 'px';
411 btn3Submit.textContent = submitLabel;
412 nav3.appendChild(btn3Back);
413 nav3.appendChild(btn3Submit);
414 sec3.appendChild(nav3);
415
416 btn3Back.addEventListener('click', function () { goToStep(2); });
417 btn3Submit.addEventListener('click', function () {
418 btn3Submit.disabled = true;
419 btn3Submit.textContent = '⟳ Processing…';
420 // Simulate submission (replace with real API call)
421 setTimeout(function () {
422 showConfirmation();
423 }, 1200);
424 });
425
426 // ── Confirmation ──
427 var successDiv = el('div', 'bkbg-bf-success');
428 successDiv.style.display = 'none';
429 card.appendChild(successDiv);
430
431 function showConfirmation() {
432 card.querySelectorAll('.bkbg-bf-header, .bkbg-bf-steps, .bkbg-bf-content').forEach(function(e){ e.style.display = 'none'; });
433 successDiv.style.display = '';
434 successDiv.innerHTML = '';
435
436 var icon = el('div', 'bkbg-bf-success-icon');
437 icon.textContent = '�
438 ';
439 var tEl = el('h2', 'bkbg-bf-success-title');
440 tEl.textContent = confTitle;
441 var mEl = el('p', 'bkbg-bf-success-msg');
442 mEl.textContent = confMsg;
443
444 var detail = el('div', 'bkbg-bf-success-detail');
445 if (showSvcSelect && selectedService) detail.innerHTML += '<p><strong>Service:</strong> ' + selectedService.label + '</p>';
446 detail.innerHTML += '<p><strong>Date:</strong> ' + formatDate(selectedDate) + '</p>';
447 detail.innerHTML += '<p><strong>Time:</strong> ' + selectedTime + '</p>';
448 if (showEmail && emailField) detail.innerHTML += '<p><strong>Confirmation sent to:</strong> ' + emailField.input.value + '</p>';
449
450 successDiv.appendChild(icon);
451 successDiv.appendChild(tEl);
452 successDiv.appendChild(mEl);
453 successDiv.appendChild(detail);
454 }
455
456 // ── Step Navigation ──
457 function goToStep(n) {
458 currentStep = n;
459 [sec1, sec2, sec3].forEach(function(s, i) {
460 s.classList.toggle('bkbg-bf-section-active', i + 1 === n);
461 });
462 stepEls.forEach(function(s, i) {
463 s.classList.remove('bkbg-bf-step-active', 'bkbg-bf-step-done');
464 if (i + 1 === n) s.classList.add('bkbg-bf-step-active');
465 if (i + 1 < n) s.classList.add('bkbg-bf-step-done');
466 });
467 if (n === 3) buildSummary();
468 }
469
470 // ── Init ──
471 renderCal();
472 renderSlots();
473 goToStep(1);
474 });
475 })();
476