feat: add Weeztix OAuth, coupon codes, and Mailwizz mapping
Implement Weeztix integration per documentation: database config and subscriber coupon_code, OAuth redirect/callback, admin setup UI with company/coupon selection via AJAX, synchronous coupon creation on public subscribe with duplicate and rate-limit handling, Mailwizz field mapping for coupon codes, subscriber table and CSV export, and connection hint on the pages list. Made-with: Cursor
This commit is contained in:
@@ -420,6 +420,7 @@ document.addEventListener('alpine:init', () => {
|
||||
redirectSecondsLeft: null,
|
||||
redirectTimer: null,
|
||||
strings: config.strings || {},
|
||||
couponCode: '',
|
||||
|
||||
copyPageLink() {
|
||||
const url = this.pageShareUrl;
|
||||
@@ -434,6 +435,19 @@ document.addEventListener('alpine:init', () => {
|
||||
});
|
||||
},
|
||||
|
||||
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();
|
||||
@@ -552,6 +566,8 @@ document.addEventListener('alpine:init', () => {
|
||||
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;
|
||||
}
|
||||
@@ -574,6 +590,7 @@ document.addEventListener('alpine:init', () => {
|
||||
fieldsUrl: cfg.fieldsUrl,
|
||||
phoneEnabled: cfg.phoneEnabled,
|
||||
hasExistingConfig: cfg.hasExistingConfig,
|
||||
hasWeeztixIntegration: cfg.hasWeeztixIntegration === true,
|
||||
existing: cfg.existing,
|
||||
csrf: cfg.csrf,
|
||||
step: 1,
|
||||
@@ -586,6 +603,7 @@ document.addEventListener('alpine:init', () => {
|
||||
fieldFirstName: '',
|
||||
fieldLastName: '',
|
||||
fieldPhone: '',
|
||||
fieldCouponCode: '',
|
||||
tagField: '',
|
||||
tagValue: '',
|
||||
loading: false,
|
||||
@@ -597,6 +615,7 @@ document.addEventListener('alpine:init', () => {
|
||||
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 ?? '';
|
||||
@@ -706,6 +725,7 @@ document.addEventListener('alpine:init', () => {
|
||||
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;
|
||||
}
|
||||
@@ -746,6 +766,104 @@ document.addEventListener('alpine:init', () => {
|
||||
this.$refs.saveForm.requestSubmit();
|
||||
},
|
||||
}));
|
||||
|
||||
Alpine.data('weeztixSetup', (cfg) => ({
|
||||
pageId: cfg.pageId,
|
||||
companiesUrl: cfg.companiesUrl,
|
||||
couponsUrl: cfg.couponsUrl,
|
||||
csrf: cfg.csrf,
|
||||
isConnected: cfg.isConnected === true,
|
||||
callbackUrl: cfg.callbackUrl,
|
||||
errorMessage: '',
|
||||
companies: [],
|
||||
coupons: [],
|
||||
companyGuid: '',
|
||||
companyName: '',
|
||||
couponGuid: '',
|
||||
couponName: '',
|
||||
codePrefix: 'PREREG',
|
||||
usageCount: 1,
|
||||
strings: cfg.strings || {},
|
||||
|
||||
async init() {
|
||||
if (cfg.existing) {
|
||||
this.codePrefix = cfg.existing.code_prefix || 'PREREG';
|
||||
this.usageCount =
|
||||
typeof cfg.existing.usage_count === 'number' ? cfg.existing.usage_count : 1;
|
||||
this.companyGuid = cfg.existing.company_guid || '';
|
||||
this.companyName = cfg.existing.company_name || '';
|
||||
this.couponGuid = cfg.existing.coupon_guid || '';
|
||||
this.couponName = cfg.existing.coupon_name || '';
|
||||
}
|
||||
if (this.isConnected) {
|
||||
await this.loadCompanies();
|
||||
if (this.companyGuid) {
|
||||
await this.loadCouponsForGuid(this.companyGuid);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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 };
|
||||
},
|
||||
|
||||
syncCompanyNameFromSelection() {
|
||||
const c = this.companies.find((x) => x.guid === this.companyGuid);
|
||||
this.companyName = c && c.name ? c.name : '';
|
||||
},
|
||||
|
||||
syncCouponName() {
|
||||
const c = this.coupons.find((x) => x.guid === this.couponGuid);
|
||||
this.couponName = c ? c.name : '';
|
||||
},
|
||||
|
||||
async loadCompanies() {
|
||||
this.errorMessage = '';
|
||||
const { res, data } = await this.postJson(this.companiesUrl, { page_id: this.pageId });
|
||||
if (!res.ok) {
|
||||
this.errorMessage = data.message || this.strings.genericError;
|
||||
return;
|
||||
}
|
||||
this.companies = Array.isArray(data.companies) ? data.companies : [];
|
||||
this.syncCompanyNameFromSelection();
|
||||
},
|
||||
|
||||
async onCompanyChange() {
|
||||
this.syncCompanyNameFromSelection();
|
||||
this.couponGuid = '';
|
||||
this.couponName = '';
|
||||
this.coupons = [];
|
||||
if (!this.companyGuid) {
|
||||
return;
|
||||
}
|
||||
await this.loadCouponsForGuid(this.companyGuid);
|
||||
},
|
||||
|
||||
async loadCouponsForGuid(guid) {
|
||||
this.errorMessage = '';
|
||||
const { res, data } = await this.postJson(this.couponsUrl, {
|
||||
page_id: this.pageId,
|
||||
company_guid: guid,
|
||||
});
|
||||
if (!res.ok) {
|
||||
this.errorMessage = data.message || this.strings.loadCouponsError;
|
||||
return;
|
||||
}
|
||||
this.coupons = Array.isArray(data.coupons) ? data.coupons : [];
|
||||
this.syncCouponName();
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
window.Alpine = Alpine;
|
||||
|
||||
Reference in New Issue
Block a user