feat(weeztix): auto company from OAuth, remove company UI
Store company_guid after OAuth via profile API; drop company select and companies endpoint. Coupons AJAX uses stored company only. Form request no longer accepts company fields from the browser. Made-with: Cursor
This commit is contained in:
@@ -13,38 +13,10 @@ use RuntimeException;
|
||||
|
||||
class WeeztixApiController extends Controller
|
||||
{
|
||||
public function companies(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'page_id' => ['required', 'integer', 'exists:preregistration_pages,id'],
|
||||
]);
|
||||
|
||||
$page = PreregistrationPage::query()->findOrFail($request->integer('page_id'));
|
||||
$this->authorize('update', $page);
|
||||
|
||||
$config = $page->weeztixConfig;
|
||||
if ($config === null || ! $config->is_connected) {
|
||||
return response()->json([
|
||||
'message' => __('Niet verbonden met Weeztix.'),
|
||||
], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$companies = (new WeeztixService($config))->getCompanies();
|
||||
|
||||
return response()->json(['companies' => $companies]);
|
||||
} catch (RuntimeException) {
|
||||
return response()->json([
|
||||
'message' => __('Kon bedrijven niet laden. Vernieuw de verbinding indien nodig.'),
|
||||
], 422);
|
||||
}
|
||||
}
|
||||
|
||||
public function coupons(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'page_id' => ['required', 'integer', 'exists:preregistration_pages,id'],
|
||||
'company_guid' => ['required', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
$page = PreregistrationPage::query()->findOrFail($request->integer('page_id'));
|
||||
@@ -57,9 +29,12 @@ class WeeztixApiController extends Controller
|
||||
], 422);
|
||||
}
|
||||
|
||||
$companyGuid = $request->string('company_guid')->toString();
|
||||
$previousGuid = $config->company_guid;
|
||||
$config->setAttribute('company_guid', $companyGuid);
|
||||
$companyGuid = $config->company_guid;
|
||||
if (! is_string($companyGuid) || $companyGuid === '') {
|
||||
return response()->json([
|
||||
'message' => __('Geen Weeztix-bedrijf gekoppeld. Verbind opnieuw met Weeztix.'),
|
||||
], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$raw = (new WeeztixService($config))->getCoupons();
|
||||
@@ -70,8 +45,6 @@ class WeeztixApiController extends Controller
|
||||
return response()->json([
|
||||
'message' => __('Kon kortingsbonnen niet laden.'),
|
||||
], 422);
|
||||
} finally {
|
||||
$config->setAttribute('company_guid', $previousGuid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,10 @@ class WeeztixOAuthController extends Controller
|
||||
try {
|
||||
$service = new WeeztixService($config);
|
||||
$service->exchangeAuthorizationCode($request->string('code')->toString());
|
||||
$config = $config->fresh();
|
||||
if ($config !== null) {
|
||||
(new WeeztixService($config))->ensureCompanyStoredFromWeeztix();
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
Log::error('Weeztix OAuth callback failed', [
|
||||
'page_id' => $page->id,
|
||||
|
||||
@@ -44,8 +44,6 @@ class UpdateWeeztixConfigRequest extends FormRequest
|
||||
'string',
|
||||
'max:2048',
|
||||
],
|
||||
'company_guid' => ['nullable', 'string', 'max:255'],
|
||||
'company_name' => ['nullable', 'string', 'max:255'],
|
||||
'coupon_guid' => ['nullable', 'string', 'max:255'],
|
||||
'coupon_name' => ['nullable', 'string', 'max:255'],
|
||||
'code_prefix' => ['nullable', 'string', 'max:32'],
|
||||
|
||||
@@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class WeeztixService
|
||||
{
|
||||
@@ -84,6 +85,42 @@ final class WeeztixService
|
||||
return $this->normalizeCompaniesFromProfile($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist company_guid when still empty after OAuth (single company per page; chosen in Weeztix login).
|
||||
* Uses the first company returned from the user profile when several are present.
|
||||
*/
|
||||
public function ensureCompanyStoredFromWeeztix(): void
|
||||
{
|
||||
$this->config->refresh();
|
||||
|
||||
if (is_string($this->config->company_guid) && $this->config->company_guid !== '') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$companies = $this->getCompanies();
|
||||
if ($companies === []) {
|
||||
Log::warning('Weeztix: geen bedrijf uit profiel voor automatische koppeling.', [
|
||||
'weeztix_config_id' => $this->config->id,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$row = $companies[0];
|
||||
$this->config->update([
|
||||
'company_guid' => $row['guid'],
|
||||
'company_name' => $row['name'],
|
||||
]);
|
||||
$this->config->refresh();
|
||||
} catch (Throwable $e) {
|
||||
Log::warning('Weeztix: automatisch bedrijf vastleggen mislukt', [
|
||||
'weeztix_config_id' => $this->config->id,
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange OAuth authorization code for tokens (admin callback).
|
||||
*/
|
||||
|
||||
@@ -769,16 +769,12 @@ document.addEventListener('alpine:init', () => {
|
||||
|
||||
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',
|
||||
@@ -797,19 +793,12 @@ document.addEventListener('alpine:init', () => {
|
||||
} else {
|
||||
this.usageCount = 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);
|
||||
}
|
||||
} 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();
|
||||
await this.loadCoupons();
|
||||
} else if (cfg.existing && cfg.existing.coupon_guid) {
|
||||
this.ensureSelectedCouponInList();
|
||||
}
|
||||
},
|
||||
@@ -829,18 +818,6 @@ document.addEventListener('alpine:init', () => {
|
||||
return { res, data };
|
||||
},
|
||||
|
||||
syncCompanyNameFromSelection() {
|
||||
if (!this.companyGuid) {
|
||||
this.companyName = '';
|
||||
return;
|
||||
}
|
||||
const c = this.companies.find((x) => x.guid === this.companyGuid);
|
||||
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() {
|
||||
if (!this.couponGuid) {
|
||||
this.couponName = '';
|
||||
@@ -852,18 +829,6 @@ document.addEventListener('alpine:init', () => {
|
||||
}
|
||||
},
|
||||
|
||||
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)) {
|
||||
@@ -876,36 +841,9 @@ document.addEventListener('alpine:init', () => {
|
||||
this.coupons = [{ guid, name: label }, ...this.coupons];
|
||||
},
|
||||
|
||||
async loadCompanies() {
|
||||
async loadCoupons() {
|
||||
this.errorMessage = '';
|
||||
const { res, data } = await this.postJson(this.companiesUrl, { page_id: this.pageId });
|
||||
if (!res.ok) {
|
||||
this.errorMessage = data.message || this.strings.genericError;
|
||||
this.ensureSelectedCompanyInList();
|
||||
return;
|
||||
}
|
||||
this.companies = Array.isArray(data.companies) ? data.companies : [];
|
||||
this.ensureSelectedCompanyInList();
|
||||
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,
|
||||
});
|
||||
const { res, data } = await this.postJson(this.couponsUrl, { page_id: this.pageId });
|
||||
if (!res.ok) {
|
||||
this.errorMessage = data.message || this.strings.loadCouponsError;
|
||||
this.ensureSelectedCouponInList();
|
||||
@@ -915,37 +853,6 @@ document.addEventListener('alpine:init', () => {
|
||||
this.ensureSelectedCouponInList();
|
||||
this.syncCouponName();
|
||||
},
|
||||
|
||||
/** Prefer human-readable label; skip API "names" that are just the GUID / UUID. */
|
||||
companyLabel(c) {
|
||||
if (!c || typeof c.guid !== 'string') {
|
||||
return '';
|
||||
}
|
||||
const g = c.guid;
|
||||
const isBadLabel = (s) => {
|
||||
const t = typeof s === 'string' ? s.trim() : '';
|
||||
return (
|
||||
t === '' ||
|
||||
t.toLowerCase() === g.toLowerCase() ||
|
||||
this.stringLooksLikeUuid(t)
|
||||
);
|
||||
};
|
||||
const fromApi = typeof c.name === 'string' ? c.name : '';
|
||||
if (!isBadLabel(fromApi)) {
|
||||
return fromApi.trim();
|
||||
}
|
||||
if (this.companyGuid === g && !isBadLabel(this.companyName)) {
|
||||
return String(this.companyName).trim();
|
||||
}
|
||||
|
||||
return g;
|
||||
},
|
||||
|
||||
stringLooksLikeUuid(s) {
|
||||
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/.test(
|
||||
String(s),
|
||||
);
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
$wz = $page->weeztixConfig;
|
||||
$existing = $wz !== null
|
||||
? [
|
||||
'company_guid' => $wz->company_guid,
|
||||
'company_name' => $wz->company_name,
|
||||
'coupon_guid' => $wz->coupon_guid,
|
||||
'coupon_name' => $wz->coupon_name,
|
||||
'code_prefix' => $wz->code_prefix,
|
||||
@@ -24,7 +22,6 @@
|
||||
class="mx-auto max-w-3xl"
|
||||
x-data="weeztixSetup(@js([
|
||||
'pageId' => $page->id,
|
||||
'companiesUrl' => route('admin.weeztix.companies'),
|
||||
'couponsUrl' => route('admin.weeztix.coupons'),
|
||||
'csrf' => csrf_token(),
|
||||
'isConnected' => $wz?->is_connected ?? false,
|
||||
@@ -33,7 +30,6 @@
|
||||
'existing' => $existing,
|
||||
'strings' => [
|
||||
'genericError' => __('Er ging iets mis. Probeer het opnieuw.'),
|
||||
'selectCompany' => __('Selecteer een bedrijf.'),
|
||||
'loadCouponsError' => __('Kon kortingsbonnen niet laden.'),
|
||||
],
|
||||
]))"
|
||||
@@ -57,6 +53,13 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($wz !== null && $wz->is_connected && ($wz->company_guid === null || $wz->company_guid === ''))
|
||||
<div class="mb-8 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-950">
|
||||
<p class="font-medium">{{ __('Bedrijf nog niet vastgelegd') }}</p>
|
||||
<p class="mt-1 text-amber-900">{{ __('Verbind opnieuw met Weeztix zodat het juiste bedrijf automatisch wordt gekoppeld.') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($wz !== null && $wz->is_connected)
|
||||
<div class="mb-8 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900">
|
||||
<p class="font-medium">{{ __('Verbonden met Weeztix') }}</p>
|
||||
@@ -154,30 +157,13 @@
|
||||
|
||||
@if ($wz !== null)
|
||||
<section class="space-y-4 pt-8">
|
||||
<h2 class="text-lg font-semibold text-slate-900">{{ __('Stap 2: Bedrijf en kortingsbon') }}</h2>
|
||||
<p class="text-sm text-slate-600">{{ __('Na een geslaagde verbinding kun je een bedrijf en bestaande coupon uit Weeztix kiezen.') }}</p>
|
||||
<h2 class="text-lg font-semibold text-slate-900">{{ __('Stap 2: Kortingsbon') }}</h2>
|
||||
<p class="text-sm text-slate-600">{{ __('Na een geslaagde verbinding kies je een bestaande coupon uit Weeztix. Het bedrijf volgt uit je Weeztix-login en wordt automatisch opgeslagen.') }}</p>
|
||||
|
||||
<form method="post" action="{{ route('admin.pages.weeztix.update', $page) }}" class="space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div>
|
||||
<label for="weeztix_company" class="block text-sm font-medium text-slate-700">{{ __('Bedrijf') }}</label>
|
||||
<select
|
||||
id="weeztix_company"
|
||||
name="company_guid"
|
||||
x-model="companyGuid"
|
||||
@change="onCompanyChange()"
|
||||
class="mt-1 block w-full rounded-lg border border-slate-300 px-3 py-2 text-slate-900 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||
>
|
||||
<option value="">{{ __('Selecteer een bedrijf…') }}</option>
|
||||
<template x-for="c in companies" :key="c.guid">
|
||||
<option :value="c.guid" x-text="companyLabel(c)"></option>
|
||||
</template>
|
||||
</select>
|
||||
<input type="hidden" name="company_name" :value="companyName">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="weeztix_coupon" class="block text-sm font-medium text-slate-700">{{ __('Coupon (kortingssjabloon)') }}</label>
|
||||
<select
|
||||
|
||||
@@ -56,7 +56,6 @@ Route::middleware(['auth', 'verified'])->prefix('admin')->name('admin.')->group(
|
||||
Route::get('pages/{page}/weeztix/oauth/redirect', [WeeztixOAuthController::class, 'redirect'])->name('pages.weeztix.oauth.redirect');
|
||||
Route::get('weeztix/callback', [WeeztixOAuthController::class, 'callback'])->name('weeztix.callback');
|
||||
|
||||
Route::post('weeztix/companies', [WeeztixApiController::class, 'companies'])->name('weeztix.companies');
|
||||
Route::post('weeztix/coupons', [WeeztixApiController::class, 'coupons'])->name('weeztix.coupons');
|
||||
|
||||
// User management (superadmin only)
|
||||
|
||||
Reference in New Issue
Block a user