fix: preserve Weeztix saved company/coupon after reload (Alpine sync)

Stop clearing DB-backed labels when API omits names; inject select options
for saved GUIDs when lists fail or omit rows; parse usage_count from JSON;
show OAuth fields hint when credentials already stored.

Made-with: Cursor
This commit is contained in:
2026-04-05 10:44:47 +02:00
parent a3158ffa34
commit 70c1d25ad4
2 changed files with 79 additions and 6 deletions

View File

@@ -788,8 +788,15 @@ document.addEventListener('alpine:init', () => {
async init() { async init() {
if (cfg.existing) { if (cfg.existing) {
this.codePrefix = cfg.existing.code_prefix || 'PREREG'; this.codePrefix = cfg.existing.code_prefix || 'PREREG';
this.usageCount = const uc = cfg.existing.usage_count;
typeof cfg.existing.usage_count === 'number' ? cfg.existing.usage_count : 1; 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.companyGuid = cfg.existing.company_guid || ''; this.companyGuid = cfg.existing.company_guid || '';
this.companyName = cfg.existing.company_name || ''; this.companyName = cfg.existing.company_name || '';
this.couponGuid = cfg.existing.coupon_guid || ''; this.couponGuid = cfg.existing.coupon_guid || '';
@@ -800,6 +807,10 @@ document.addEventListener('alpine:init', () => {
if (this.companyGuid) { if (this.companyGuid) {
await this.loadCouponsForGuid(this.companyGuid); await this.loadCouponsForGuid(this.companyGuid);
} }
} else if (cfg.existing && (cfg.existing.company_guid || cfg.existing.coupon_guid)) {
// Show saved choices even when not connected (e.g. expired refresh); lists are from DB only.
this.ensureSelectedCompanyInList();
this.ensureSelectedCouponInList();
} }
}, },
@@ -819,13 +830,50 @@ document.addEventListener('alpine:init', () => {
}, },
syncCompanyNameFromSelection() { syncCompanyNameFromSelection() {
if (!this.companyGuid) {
this.companyName = '';
return;
}
const c = this.companies.find((x) => x.guid === this.companyGuid); const c = this.companies.find((x) => x.guid === this.companyGuid);
this.companyName = c && c.name ? c.name : ''; if (c && typeof c.name === 'string' && c.name.trim() !== '') {
this.companyName = c.name.trim();
}
// If API row has no name or list is still loading, keep companyName from server (DB).
}, },
syncCouponName() { syncCouponName() {
if (!this.couponGuid) {
this.couponName = '';
return;
}
const c = this.coupons.find((x) => x.guid === this.couponGuid); const c = this.coupons.find((x) => x.guid === this.couponGuid);
this.couponName = c ? c.name : ''; if (c && typeof c.name === 'string' && c.name.trim() !== '') {
this.couponName = c.name.trim();
}
},
ensureSelectedCompanyInList() {
const guid = this.companyGuid;
if (!guid || this.companies.some((x) => x.guid === guid)) {
return;
}
const label =
typeof this.companyName === 'string' && this.companyName.trim() !== ''
? this.companyName.trim()
: guid;
this.companies = [{ guid, name: label }, ...this.companies];
},
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 loadCompanies() { async loadCompanies() {
@@ -833,9 +881,11 @@ document.addEventListener('alpine:init', () => {
const { res, data } = await this.postJson(this.companiesUrl, { page_id: this.pageId }); const { res, data } = await this.postJson(this.companiesUrl, { page_id: this.pageId });
if (!res.ok) { if (!res.ok) {
this.errorMessage = data.message || this.strings.genericError; this.errorMessage = data.message || this.strings.genericError;
this.ensureSelectedCompanyInList();
return; return;
} }
this.companies = Array.isArray(data.companies) ? data.companies : []; this.companies = Array.isArray(data.companies) ? data.companies : [];
this.ensureSelectedCompanyInList();
this.syncCompanyNameFromSelection(); this.syncCompanyNameFromSelection();
}, },
@@ -858,11 +908,26 @@ document.addEventListener('alpine:init', () => {
}); });
if (!res.ok) { if (!res.ok) {
this.errorMessage = data.message || this.strings.loadCouponsError; this.errorMessage = data.message || this.strings.loadCouponsError;
this.ensureSelectedCouponInList();
return; return;
} }
this.coupons = Array.isArray(data.coupons) ? data.coupons : []; this.coupons = Array.isArray(data.coupons) ? data.coupons : [];
this.ensureSelectedCouponInList();
this.syncCouponName(); this.syncCouponName();
}, },
/** Prefer API/display name; avoid showing raw GUID when a label exists. */
companyLabel(c) {
if (!c || typeof c.guid !== 'string') {
return '';
}
const n = c.name;
if (typeof n === 'string' && n.trim() !== '') {
return n.trim();
}
return c.guid;
},
})); }));
}); });

View File

@@ -62,9 +62,12 @@
<p class="font-medium">{{ __('Verbonden met Weeztix') }}</p> <p class="font-medium">{{ __('Verbonden met Weeztix') }}</p>
@if ($wz->token_expires_at) @if ($wz->token_expires_at)
<p class="mt-1 text-emerald-800"> <p class="mt-1 text-emerald-800">
{{ __('Toegangstoken verloopt om:') }} {{ __('Huidig toegangstoken tot:') }}
<span class="font-mono text-xs">{{ $wz->token_expires_at->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</span> <span class="font-mono text-xs">{{ $wz->token_expires_at->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</span>
</p> </p>
<p class="mt-2 text-emerald-800/90">
{{ __('Het toegangstoken wordt automatisch vernieuwd bij API-gebruik (o.a. kortingscodes), zolang de refresh-token geldig is. Je hoeft niet opnieuw te verbinden.') }}
</p>
@endif @endif
</div> </div>
@elseif ($wz !== null && ! $wz->is_connected) @elseif ($wz !== null && ! $wz->is_connected)
@@ -127,6 +130,11 @@
@if ($wz === null) required @endif @if ($wz === null) required @endif
> >
</div> </div>
@if ($wz !== null)
<p class="text-xs text-slate-500">
{{ __('Client ID en secret zijn opgeslagen maar worden niet opnieuw getoond. Laat de velden leeg om ze te behouden; vul ze alleen in als je ze wilt wijzigen.') }}
</p>
@endif
<button type="submit" class="rounded-lg bg-slate-800 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-700"> <button type="submit" class="rounded-lg bg-slate-800 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-700">
{{ __('Gegevens opslaan') }} {{ __('Gegevens opslaan') }}
</button> </button>
@@ -164,7 +172,7 @@
> >
<option value="">{{ __('Selecteer een bedrijf…') }}</option> <option value="">{{ __('Selecteer een bedrijf…') }}</option>
<template x-for="c in companies" :key="c.guid"> <template x-for="c in companies" :key="c.guid">
<option :value="c.guid" x-text="(c.name || c.guid)"></option> <option :value="c.guid" x-text="companyLabel(c)"></option>
</template> </template>
</select> </select>
<input type="hidden" name="company_name" :value="companyName"> <input type="hidden" name="company_name" :value="companyName">