875 lines
29 KiB
JavaScript
875 lines
29 KiB
JavaScript
import './bootstrap';
|
|
|
|
import Alpine from 'alpinejs';
|
|
import Sortable from 'sortablejs';
|
|
|
|
/**
|
|
* Client-side email check (aligned loosely with RFC-style rules; server is authoritative).
|
|
*/
|
|
function isValidEmailFormat(value) {
|
|
const email = String(value).trim().toLowerCase();
|
|
if (email.length < 5 || email.length > 255) {
|
|
return false;
|
|
}
|
|
if (!email.includes('@')) {
|
|
return false;
|
|
}
|
|
const at = email.lastIndexOf('@');
|
|
const local = email.slice(0, at);
|
|
const domain = email.slice(at + 1);
|
|
if (!local || !domain || local.includes('..') || domain.includes('..')) {
|
|
return false;
|
|
}
|
|
if (domain.startsWith('.') || domain.endsWith('.')) {
|
|
return false;
|
|
}
|
|
if (!/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+$/i.test(local)) {
|
|
return false;
|
|
}
|
|
if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i.test(domain)) {
|
|
return false;
|
|
}
|
|
const tld = domain.split('.').pop();
|
|
return Boolean(tld && tld.length >= 2);
|
|
}
|
|
|
|
function newBlockUid() {
|
|
return `n_${crypto.randomUUID().replaceAll('-', '')}`;
|
|
}
|
|
|
|
function deepClone(o) {
|
|
return JSON.parse(JSON.stringify(o));
|
|
}
|
|
|
|
/** Default `content` payloads for admin block editor (Dutch defaults). */
|
|
function pageBlockDefaultContent(type) {
|
|
const defaults = {
|
|
hero: {
|
|
headline: '',
|
|
subheadline: '',
|
|
eyebrow_text: '',
|
|
eyebrow_style: 'badge',
|
|
text_alignment: 'center',
|
|
},
|
|
image: {
|
|
image: null,
|
|
link_url: '',
|
|
alt: '',
|
|
max_width_px: 320,
|
|
text_alignment: 'center',
|
|
},
|
|
benefits: {
|
|
title: 'Waarom voorregistreren?',
|
|
items: [
|
|
{ icon: 'ticket', text: 'Exclusieve korting op tickets' },
|
|
{ icon: 'clock', text: 'Eerder toegang tot de ticketshop' },
|
|
],
|
|
layout: 'list',
|
|
max_columns: 2,
|
|
},
|
|
social_proof: {
|
|
template: 'Al {count} bezoekers aangemeld!',
|
|
min_count: 10,
|
|
show_animation: true,
|
|
style: 'pill',
|
|
},
|
|
form: {
|
|
title: 'Registreer nu',
|
|
description: '',
|
|
button_label: 'Registreer nu!',
|
|
button_color: '#F47B20',
|
|
button_text_color: '#FFFFFF',
|
|
fields: {
|
|
first_name: {
|
|
enabled: true,
|
|
required: true,
|
|
label: 'Voornaam',
|
|
placeholder: 'Je voornaam',
|
|
},
|
|
last_name: {
|
|
enabled: true,
|
|
required: true,
|
|
label: 'Achternaam',
|
|
placeholder: 'Je achternaam',
|
|
},
|
|
email: {
|
|
enabled: true,
|
|
required: true,
|
|
label: 'E-mailadres',
|
|
placeholder: 'je@email.nl',
|
|
},
|
|
phone: {
|
|
enabled: false,
|
|
required: false,
|
|
label: 'Mobiel',
|
|
placeholder: '+31 6 12345678',
|
|
},
|
|
},
|
|
show_field_icons: true,
|
|
privacy_text: 'Door je te registreren ga je akkoord met onze privacyverklaring.',
|
|
privacy_url: '',
|
|
},
|
|
countdown: {
|
|
target_datetime: new Date(Date.now() + 86400000).toISOString().slice(0, 16),
|
|
title: 'De pre-registratie opent over:',
|
|
expired_action: 'reload',
|
|
expired_message: '',
|
|
style: 'large',
|
|
show_labels: true,
|
|
labels: {
|
|
days: 'dagen',
|
|
hours: 'uren',
|
|
minutes: 'minuten',
|
|
seconds: 'seconden',
|
|
},
|
|
},
|
|
text: {
|
|
title: '',
|
|
body: '',
|
|
text_size: 'base',
|
|
text_alignment: 'center',
|
|
},
|
|
cta_banner: {
|
|
text: '',
|
|
button_label: 'Ga naar de ticketshop',
|
|
button_url: 'https://',
|
|
button_color: '#F47B20',
|
|
style: 'inline',
|
|
},
|
|
divider: {
|
|
style: 'line',
|
|
spacing: 'medium',
|
|
},
|
|
};
|
|
return deepClone(defaults[type] || { _: '' });
|
|
}
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('pageBlockEditor', (config) => ({
|
|
blocks: config.initialBlocks || [],
|
|
blockTypes: config.blockTypes || [],
|
|
storageBase: typeof config.storageBase === 'string' ? config.storageBase.replace(/\/$/, '') : '',
|
|
sortable: null,
|
|
collapsed: {},
|
|
confirmDeleteUid: null,
|
|
_verticalDragLock: null,
|
|
|
|
init() {
|
|
this.blocks.forEach((b) => {
|
|
this.collapsed[b.uid] = true;
|
|
});
|
|
this.$nextTick(() => {
|
|
const el = this.$refs.sortRoot;
|
|
if (!el) {
|
|
return;
|
|
}
|
|
const lockGhostToVerticalAxis = () => {
|
|
requestAnimationFrame(() => {
|
|
const ghost = Sortable.ghost;
|
|
if (!ghost) {
|
|
return;
|
|
}
|
|
const raw = window.getComputedStyle(ghost).transform;
|
|
if (!raw || raw === 'none') {
|
|
return;
|
|
}
|
|
try {
|
|
const m = new DOMMatrix(raw);
|
|
if (m.m41 !== 0) {
|
|
m.m41 = 0;
|
|
ghost.style.transform = m.toString();
|
|
}
|
|
} catch {
|
|
/* ignore invalid matrix */
|
|
}
|
|
});
|
|
};
|
|
this.sortable = Sortable.create(el, {
|
|
handle: '.block-drag-handle',
|
|
animation: 150,
|
|
direction: 'vertical',
|
|
forceFallback: true,
|
|
fallbackOnBody: true,
|
|
onStart: () => {
|
|
this._verticalDragLock = lockGhostToVerticalAxis;
|
|
document.addEventListener('mousemove', this._verticalDragLock, false);
|
|
document.addEventListener('touchmove', this._verticalDragLock, { passive: true });
|
|
},
|
|
onEnd: () => {
|
|
if (this._verticalDragLock) {
|
|
document.removeEventListener('mousemove', this._verticalDragLock, false);
|
|
document.removeEventListener('touchmove', this._verticalDragLock, { passive: true });
|
|
this._verticalDragLock = null;
|
|
}
|
|
this.syncSortOrderFromDom();
|
|
},
|
|
});
|
|
});
|
|
},
|
|
|
|
syncSortOrderFromDom() {
|
|
const root = this.$refs.sortRoot;
|
|
if (!root) {
|
|
return;
|
|
}
|
|
const rows = [...root.querySelectorAll('[data-block-uid]')];
|
|
rows.forEach((row, i) => {
|
|
const uid = row.getAttribute('data-block-uid');
|
|
const b = this.blocks.find((x) => x.uid === uid);
|
|
if (b) {
|
|
b.sort_order = i;
|
|
}
|
|
});
|
|
},
|
|
|
|
hasFormBlock() {
|
|
return this.blocks.some((b) => b.type === 'form');
|
|
},
|
|
|
|
toggleCollapsed(uid) {
|
|
this.collapsed[uid] = !this.collapsed[uid];
|
|
},
|
|
|
|
isCollapsed(uid) {
|
|
return !!this.collapsed[uid];
|
|
},
|
|
|
|
collapseAll() {
|
|
this.blocks.forEach((b) => {
|
|
this.collapsed[b.uid] = true;
|
|
});
|
|
},
|
|
|
|
expandAll() {
|
|
this.blocks.forEach((b) => {
|
|
this.collapsed[b.uid] = false;
|
|
});
|
|
},
|
|
|
|
blockSummary(block) {
|
|
const c = block.content || {};
|
|
const pick = (...keys) => {
|
|
for (const k of keys) {
|
|
const v = c[k];
|
|
if (typeof v === 'string' && v.trim() !== '') {
|
|
return v.trim().slice(0, 88);
|
|
}
|
|
}
|
|
return '';
|
|
};
|
|
switch (block.type) {
|
|
case 'hero':
|
|
return pick('headline');
|
|
case 'benefits':
|
|
return pick('title');
|
|
case 'form':
|
|
return pick('title');
|
|
case 'countdown':
|
|
return pick('title');
|
|
case 'text':
|
|
return pick('title') || pick('body');
|
|
case 'cta_banner':
|
|
return pick('text') || pick('button_label');
|
|
case 'social_proof':
|
|
return pick('template');
|
|
case 'image':
|
|
return pick('alt');
|
|
default:
|
|
return '';
|
|
}
|
|
},
|
|
|
|
addBlock(type) {
|
|
if (type === 'form' && this.hasFormBlock()) {
|
|
return;
|
|
}
|
|
const nextOrder =
|
|
this.blocks.length === 0
|
|
? 0
|
|
: Math.max(...this.blocks.map((b) => Number(b.sort_order) || 0)) + 1;
|
|
const uid = newBlockUid();
|
|
this.blocks.push({
|
|
uid,
|
|
type,
|
|
sort_order: nextOrder,
|
|
is_visible: true,
|
|
content: pageBlockDefaultContent(type),
|
|
});
|
|
this.collapsed[uid] = false;
|
|
this.$nextTick(() => this.syncSortOrderFromDom());
|
|
},
|
|
|
|
removeBlock(uid) {
|
|
this.blocks = this.blocks.filter((b) => b.uid !== uid);
|
|
delete this.collapsed[uid];
|
|
this.confirmDeleteUid = null;
|
|
this.$nextTick(() => this.syncSortOrderFromDom());
|
|
},
|
|
|
|
requestDelete(uid) {
|
|
this.confirmDeleteUid = uid;
|
|
},
|
|
|
|
cancelDelete() {
|
|
this.confirmDeleteUid = null;
|
|
},
|
|
|
|
addBenefitItem(uid) {
|
|
const b = this.blocks.find((x) => x.uid === uid);
|
|
if (!b || b.type !== 'benefits') {
|
|
return;
|
|
}
|
|
if (!Array.isArray(b.content.items)) {
|
|
b.content.items = [];
|
|
}
|
|
b.content.items.push({ icon: 'check', text: '' });
|
|
},
|
|
|
|
removeBenefitItem(uid, idx) {
|
|
const b = this.blocks.find((x) => x.uid === uid);
|
|
if (!b || !Array.isArray(b.content.items)) {
|
|
return;
|
|
}
|
|
b.content.items.splice(idx, 1);
|
|
},
|
|
}));
|
|
|
|
Alpine.data('countdownBlock', (cfg) => ({
|
|
targetMs: cfg.targetMs,
|
|
expired: false,
|
|
days: 0,
|
|
hours: 0,
|
|
minutes: 0,
|
|
seconds: 0,
|
|
showLabels: cfg.showLabels !== false,
|
|
labels: cfg.labels || {},
|
|
expiredAction: cfg.expiredAction || 'hide',
|
|
expiredMessage: cfg.expiredMessage || '',
|
|
timer: null,
|
|
|
|
init() {
|
|
this.tick();
|
|
this.timer = setInterval(() => this.tick(), 1000);
|
|
},
|
|
|
|
destroy() {
|
|
if (this.timer !== null) {
|
|
clearInterval(this.timer);
|
|
}
|
|
},
|
|
|
|
tick() {
|
|
const diff = this.targetMs - Date.now();
|
|
if (diff <= 0) {
|
|
if (this.timer !== null) {
|
|
clearInterval(this.timer);
|
|
this.timer = null;
|
|
}
|
|
this.expired = true;
|
|
if (this.expiredAction === 'hide' && this.$el) {
|
|
this.$el.classList.add('hidden');
|
|
}
|
|
if (this.expiredAction === 'reload') {
|
|
window.location.reload();
|
|
}
|
|
return;
|
|
}
|
|
const totalSeconds = Math.floor(diff / 1000);
|
|
this.days = Math.floor(totalSeconds / 86400);
|
|
this.hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
this.minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
this.seconds = totalSeconds % 60;
|
|
},
|
|
|
|
pad(n) {
|
|
return String(n).padStart(2, '0');
|
|
},
|
|
}));
|
|
|
|
Alpine.data('publicPreregisterPage', (config) => ({
|
|
phase: config.phase,
|
|
startAtMs: config.startAtMs,
|
|
phoneEnabled: config.phoneEnabled,
|
|
phoneRequired: config.phoneRequired === true,
|
|
subscribeUrl: config.subscribeUrl,
|
|
csrfToken: config.csrfToken,
|
|
genericError: config.genericError,
|
|
labelDay: config.labelDay,
|
|
labelDays: config.labelDays,
|
|
invalidEmailMsg: config.invalidEmailMsg,
|
|
invalidPhoneMsg: config.invalidPhoneMsg,
|
|
days: 0,
|
|
hours: 0,
|
|
minutes: 0,
|
|
seconds: 0,
|
|
countdownTimer: null,
|
|
first_name: '',
|
|
last_name: '',
|
|
email: '',
|
|
phone: '',
|
|
submitting: false,
|
|
formError: '',
|
|
fieldErrors: {},
|
|
thankYouMessage: '',
|
|
formButtonLabel: config.formButtonLabel || '',
|
|
formButtonColor: config.formButtonColor || '#F47B20',
|
|
formButtonTextColor: config.formButtonTextColor || '#FFFFFF',
|
|
pageShareUrl: config.pageShareUrl || '',
|
|
copyFeedback: '',
|
|
redirectUrl: config.redirectUrl || '',
|
|
redirectSecondsLeft: null,
|
|
redirectTimer: null,
|
|
strings: config.strings || {},
|
|
couponCode: '',
|
|
|
|
copyPageLink() {
|
|
const url = this.pageShareUrl;
|
|
if (!url) {
|
|
return;
|
|
}
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
this.copyFeedback = config.strings?.linkCopied || '';
|
|
setTimeout(() => {
|
|
this.copyFeedback = '';
|
|
}, 2500);
|
|
});
|
|
},
|
|
|
|
copyCouponCode() {
|
|
const code = this.couponCode;
|
|
if (!code) {
|
|
return;
|
|
}
|
|
navigator.clipboard.writeText(code).then(() => {
|
|
this.copyFeedback = this.strings?.couponCopied || '';
|
|
setTimeout(() => {
|
|
this.copyFeedback = '';
|
|
}, 2500);
|
|
});
|
|
},
|
|
|
|
init() {
|
|
if (this.phase === 'before') {
|
|
this.tickCountdown();
|
|
this.countdownTimer = setInterval(() => this.tickCountdown(), 1000);
|
|
}
|
|
},
|
|
|
|
destroy() {
|
|
if (this.countdownTimer !== null) {
|
|
clearInterval(this.countdownTimer);
|
|
this.countdownTimer = null;
|
|
}
|
|
if (this.redirectTimer !== null) {
|
|
clearInterval(this.redirectTimer);
|
|
this.redirectTimer = null;
|
|
}
|
|
},
|
|
|
|
startRedirectCountdownIfNeeded() {
|
|
if (!this.redirectUrl || String(this.redirectUrl).trim() === '') {
|
|
return;
|
|
}
|
|
this.redirectSecondsLeft = 5;
|
|
this.redirectTimer = setInterval(() => {
|
|
this.redirectSecondsLeft--;
|
|
if (this.redirectSecondsLeft <= 0) {
|
|
if (this.redirectTimer !== null) {
|
|
clearInterval(this.redirectTimer);
|
|
this.redirectTimer = null;
|
|
}
|
|
window.location.assign(String(this.redirectUrl));
|
|
}
|
|
}, 1000);
|
|
},
|
|
|
|
tickCountdown() {
|
|
const start = this.startAtMs;
|
|
const now = Date.now();
|
|
const diff = start - now;
|
|
if (diff <= 0) {
|
|
if (this.countdownTimer !== null) {
|
|
clearInterval(this.countdownTimer);
|
|
this.countdownTimer = null;
|
|
}
|
|
this.phase = 'active';
|
|
return;
|
|
}
|
|
const totalSeconds = Math.floor(diff / 1000);
|
|
this.days = Math.floor(totalSeconds / 86400);
|
|
this.hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
this.minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
this.seconds = totalSeconds % 60;
|
|
},
|
|
|
|
pad(n) {
|
|
return String(n).padStart(2, '0');
|
|
},
|
|
|
|
validateEmailAndPhone() {
|
|
this.fieldErrors = {};
|
|
let ok = true;
|
|
if (!isValidEmailFormat(this.email)) {
|
|
this.fieldErrors.email = [this.invalidEmailMsg];
|
|
ok = false;
|
|
}
|
|
if (this.phoneEnabled) {
|
|
const trimmed = String(this.phone).trim();
|
|
if (this.phoneRequired && trimmed === '') {
|
|
this.fieldErrors.phone = [this.invalidPhoneMsg];
|
|
ok = false;
|
|
} else if (trimmed !== '') {
|
|
const digits = trimmed.replace(/\D/g, '');
|
|
if (digits.length < 8 || digits.length > 15) {
|
|
this.fieldErrors.phone = [this.invalidPhoneMsg];
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
},
|
|
|
|
async submitForm() {
|
|
this.formError = '';
|
|
this.fieldErrors = {};
|
|
if (!this.validateEmailAndPhone()) {
|
|
return;
|
|
}
|
|
|
|
const form = this.$refs.form;
|
|
if (form !== undefined && form !== null && !form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
this.submitting = true;
|
|
try {
|
|
const body = {
|
|
first_name: this.first_name,
|
|
last_name: this.last_name,
|
|
email: this.email,
|
|
};
|
|
if (this.phoneEnabled) {
|
|
body.phone = this.phone;
|
|
}
|
|
const res = await fetch(this.subscribeUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
'X-CSRF-TOKEN': this.csrfToken,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
const data = await res.json().catch(() => ({}));
|
|
if (res.ok && data.success) {
|
|
this.phase = 'thanks';
|
|
this.thankYouMessage = data.message ?? '';
|
|
this.couponCode =
|
|
typeof data.coupon_code === 'string' && data.coupon_code !== '' ? data.coupon_code : '';
|
|
this.startRedirectCountdownIfNeeded();
|
|
return;
|
|
}
|
|
if (typeof data.message === 'string' && data.message !== '') {
|
|
this.formError = data.message;
|
|
}
|
|
if (data.errors !== undefined && data.errors !== null && typeof data.errors === 'object') {
|
|
this.fieldErrors = data.errors;
|
|
}
|
|
} catch {
|
|
this.formError = this.genericError;
|
|
} finally {
|
|
this.submitting = false;
|
|
}
|
|
},
|
|
}));
|
|
|
|
Alpine.data('mailwizzWizard', (cfg) => ({
|
|
listsUrl: cfg.listsUrl,
|
|
fieldsUrl: cfg.fieldsUrl,
|
|
phoneEnabled: cfg.phoneEnabled,
|
|
hasExistingConfig: cfg.hasExistingConfig,
|
|
hasWeeztixIntegration: cfg.hasWeeztixIntegration === true,
|
|
existing: cfg.existing,
|
|
csrf: cfg.csrf,
|
|
step: 1,
|
|
apiKey: '',
|
|
lists: [],
|
|
selectedListUid: '',
|
|
selectedListName: '',
|
|
allFields: [],
|
|
fieldEmail: '',
|
|
fieldFirstName: '',
|
|
fieldLastName: '',
|
|
fieldPhone: '',
|
|
fieldCouponCode: '',
|
|
tagField: '',
|
|
tagValue: '',
|
|
loading: false,
|
|
errorMessage: '',
|
|
|
|
init() {
|
|
if (this.existing) {
|
|
this.fieldEmail = this.existing.field_email ?? '';
|
|
this.fieldFirstName = this.existing.field_first_name ?? '';
|
|
this.fieldLastName = this.existing.field_last_name ?? '';
|
|
this.fieldPhone = this.existing.field_phone ?? '';
|
|
this.fieldCouponCode = this.existing.field_coupon_code ?? '';
|
|
this.tagField = this.existing.tag_field ?? '';
|
|
this.tagValue = this.existing.tag_value ?? '';
|
|
this.selectedListUid = this.existing.list_uid ?? '';
|
|
this.selectedListName = this.existing.list_name ?? '';
|
|
}
|
|
},
|
|
|
|
textFields() {
|
|
return this.allFields.filter((f) => f.type_identifier === 'text');
|
|
},
|
|
|
|
emailFieldChoices() {
|
|
const texts = this.textFields();
|
|
const tagged = texts.filter(
|
|
(f) =>
|
|
(f.tag && f.tag.toUpperCase().includes('EMAIL')) ||
|
|
(f.label && f.label.toLowerCase().includes('email')),
|
|
);
|
|
return tagged.length > 0 ? tagged : texts;
|
|
},
|
|
|
|
phoneFields() {
|
|
return this.allFields.filter((f) => f.type_identifier === 'phonenumber');
|
|
},
|
|
|
|
checkboxFields() {
|
|
return this.allFields.filter((f) => f.type_identifier === 'checkboxlist');
|
|
},
|
|
|
|
tagOptionsList() {
|
|
const f = this.allFields.find((x) => x.tag === this.tagField);
|
|
if (!f || !f.options) {
|
|
return [];
|
|
}
|
|
return Object.entries(f.options).map(([key, label]) => ({
|
|
key,
|
|
label: String(label),
|
|
}));
|
|
},
|
|
|
|
async postJson(url, body) {
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
'X-CSRF-TOKEN': this.csrf,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
const data = await res.json().catch(() => ({}));
|
|
return { res, data };
|
|
},
|
|
|
|
async connectLists() {
|
|
this.errorMessage = '';
|
|
if (!this.apiKey.trim()) {
|
|
this.errorMessage = cfg.strings.apiKeyRequired;
|
|
return;
|
|
}
|
|
this.loading = true;
|
|
try {
|
|
const { res, data } = await this.postJson(this.listsUrl, { api_key: this.apiKey });
|
|
if (!res.ok) {
|
|
this.errorMessage = data.message || data.error || cfg.strings.genericError;
|
|
return;
|
|
}
|
|
this.lists = Array.isArray(data.lists) ? data.lists : [];
|
|
if (this.lists.length === 0) {
|
|
this.errorMessage = cfg.strings.noListsError;
|
|
return;
|
|
}
|
|
if (this.existing?.list_uid) {
|
|
const match = this.lists.find((l) => l.list_uid === this.existing.list_uid);
|
|
if (match) {
|
|
this.selectedListUid = match.list_uid;
|
|
this.selectedListName = match.name;
|
|
}
|
|
}
|
|
this.step = 2;
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async loadFieldsAndGoStep3() {
|
|
this.errorMessage = '';
|
|
if (!this.selectedListUid) {
|
|
this.errorMessage = cfg.strings.selectListError;
|
|
return;
|
|
}
|
|
this.syncListNameFromSelection();
|
|
this.loading = true;
|
|
try {
|
|
const { res, data } = await this.postJson(this.fieldsUrl, {
|
|
api_key: this.apiKey,
|
|
list_uid: this.selectedListUid,
|
|
});
|
|
if (!res.ok) {
|
|
this.errorMessage = data.message || data.error || cfg.strings.genericError;
|
|
return;
|
|
}
|
|
this.allFields = Array.isArray(data.fields) ? data.fields : [];
|
|
if (this.existing) {
|
|
this.fieldEmail = this.existing.field_email || this.fieldEmail;
|
|
this.fieldFirstName = this.existing.field_first_name || this.fieldFirstName;
|
|
this.fieldLastName = this.existing.field_last_name || this.fieldLastName;
|
|
this.fieldPhone = this.existing.field_phone || this.fieldPhone;
|
|
this.fieldCouponCode = this.existing.field_coupon_code || this.fieldCouponCode;
|
|
this.tagField = this.existing.tag_field || this.tagField;
|
|
this.tagValue = this.existing.tag_value || this.tagValue;
|
|
}
|
|
this.step = 3;
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
syncListNameFromSelection() {
|
|
const l = this.lists.find((x) => x.list_uid === this.selectedListUid);
|
|
this.selectedListName = l ? l.name : '';
|
|
},
|
|
|
|
goStep4() {
|
|
this.errorMessage = '';
|
|
if (!this.fieldEmail || !this.fieldFirstName || !this.fieldLastName) {
|
|
this.errorMessage = cfg.strings.mapFieldsError;
|
|
return;
|
|
}
|
|
if (!this.tagField) {
|
|
this.errorMessage = cfg.strings.tagFieldError;
|
|
return;
|
|
}
|
|
this.step = 4;
|
|
},
|
|
|
|
submitSave() {
|
|
this.errorMessage = '';
|
|
if (!this.tagValue) {
|
|
this.errorMessage = cfg.strings.tagValueError;
|
|
return;
|
|
}
|
|
if (!this.hasExistingConfig && !this.apiKey.trim()) {
|
|
this.errorMessage = cfg.strings.apiKeyRequired;
|
|
return;
|
|
}
|
|
this.$refs.saveForm.requestSubmit();
|
|
},
|
|
}));
|
|
|
|
Alpine.data('weeztixSetup', (cfg) => ({
|
|
pageId: cfg.pageId,
|
|
couponsUrl: cfg.couponsUrl,
|
|
csrf: cfg.csrf,
|
|
isConnected: cfg.isConnected === true,
|
|
callbackUrl: cfg.callbackUrl,
|
|
errorMessage: '',
|
|
coupons: [],
|
|
couponGuid: '',
|
|
couponName: '',
|
|
codePrefix: 'PREREG',
|
|
usageCount: 1,
|
|
couponsRefreshing: false,
|
|
strings: cfg.strings || {},
|
|
|
|
async init() {
|
|
if (cfg.existing) {
|
|
this.codePrefix = cfg.existing.code_prefix || 'PREREG';
|
|
const uc = cfg.existing.usage_count;
|
|
if (typeof uc === 'number' && !Number.isNaN(uc)) {
|
|
this.usageCount = uc;
|
|
} else if (uc !== null && uc !== undefined && String(uc).trim() !== '') {
|
|
const parsed = parseInt(String(uc), 10);
|
|
this.usageCount = Number.isNaN(parsed) ? 1 : parsed;
|
|
} else {
|
|
this.usageCount = 1;
|
|
}
|
|
this.couponGuid = cfg.existing.coupon_guid || '';
|
|
this.couponName = cfg.existing.coupon_name || '';
|
|
}
|
|
if (this.isConnected) {
|
|
await this.loadCoupons();
|
|
} else if (cfg.existing && cfg.existing.coupon_guid) {
|
|
this.ensureSelectedCouponInList();
|
|
}
|
|
},
|
|
|
|
async postJson(url, body) {
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
'X-CSRF-TOKEN': this.csrf,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
const data = await res.json().catch(() => ({}));
|
|
return { res, data };
|
|
},
|
|
|
|
syncCouponName() {
|
|
if (!this.couponGuid) {
|
|
this.couponName = '';
|
|
return;
|
|
}
|
|
const c = this.coupons.find((x) => x.guid === this.couponGuid);
|
|
if (c && typeof c.name === 'string' && c.name.trim() !== '') {
|
|
this.couponName = c.name.trim();
|
|
}
|
|
},
|
|
|
|
ensureSelectedCouponInList() {
|
|
const guid = this.couponGuid;
|
|
if (!guid || this.coupons.some((x) => x.guid === guid)) {
|
|
return;
|
|
}
|
|
const label =
|
|
typeof this.couponName === 'string' && this.couponName.trim() !== ''
|
|
? this.couponName.trim()
|
|
: guid;
|
|
this.coupons = [{ guid, name: label }, ...this.coupons];
|
|
},
|
|
|
|
async loadCoupons() {
|
|
this.errorMessage = '';
|
|
const { res, data } = await this.postJson(this.couponsUrl, { page_id: this.pageId });
|
|
if (!res.ok) {
|
|
this.errorMessage = data.message || this.strings.loadCouponsError;
|
|
this.ensureSelectedCouponInList();
|
|
return;
|
|
}
|
|
this.coupons = Array.isArray(data.coupons) ? data.coupons : [];
|
|
this.ensureSelectedCouponInList();
|
|
this.syncCouponName();
|
|
},
|
|
|
|
async refreshCoupons() {
|
|
if (!this.isConnected) {
|
|
return;
|
|
}
|
|
this.couponsRefreshing = true;
|
|
try {
|
|
await this.loadCoupons();
|
|
} finally {
|
|
this.couponsRefreshing = false;
|
|
}
|
|
},
|
|
}));
|
|
});
|
|
|
|
window.Alpine = Alpine;
|
|
|
|
Alpine.start();
|