Files
bert.hausmans 217e1d9afb fix(weeztix): allow OAuth reconnect in wizard step 2 and re-sync company
Always sync company from profile after OAuth; remove skip when company_guid
was already set. Step 2 shows reconnect for connected users plus link to step 3.

Made-with: Cursor
2026-04-05 11:16:49 +02:00

352 lines
23 KiB
PHP

@php
use Illuminate\Support\Carbon;
$wz = $page->weeztixConfig;
$existing = $wz !== null
? [
'coupon_guid' => $wz->coupon_guid,
'coupon_name' => $wz->coupon_name,
'code_prefix' => $wz->code_prefix,
'usage_count' => $wz->usage_count,
]
: null;
$credentialsEdit = ! $hasStoredCredentials || request()->query('credentials') === 'edit';
$oauthUrl = route('admin.pages.weeztix.oauth.redirect', ['page' => $page, 'wizard' => 1]);
@endphp
@extends('layouts.admin')
@section('title', __('Weeztix') . ' — ' . $page->title)
@section('mobile_title', __('Weeztix'))
@section('content')
<div class="mx-auto max-w-3xl">
<div class="mb-8">
<a href="{{ route('admin.pages.edit', $page) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-500"> {{ __('Terug naar pagina') }}</a>
<h1 class="mt-4 text-2xl font-semibold text-slate-900">{{ __('Weeztix') }}</h1>
<p class="mt-2 text-sm text-slate-600">{{ __('Pagina:') }} <span class="font-medium text-slate-800">{{ $page->title }}</span></p>
</div>
@include('admin.pages._save_flash')
@if ($errors->any())
<div class="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-900" role="alert">
<p class="font-medium">{{ __('Controleer het volgende:') }}</p>
<ul class="mt-2 list-disc space-y-1 pl-5">
@foreach ($errors->all() as $message)
<li>{{ $message }}</li>
@endforeach
</ul>
</div>
@endif
@if (! $showWizard && $wz !== null)
{{-- Summary (read-only): change only via wizard --}}
@if ($wz->is_connected && ($wz->company_guid === null || $wz->company_guid === ''))
<div class="mb-6 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">{{ __('Start de wizard en verbind opnieuw met Weeztix zodat het bedrijf automatisch wordt gekoppeld.') }}</p>
</div>
@endif
<div class="mb-6 flex flex-wrap items-center gap-3">
<a
href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 1]) }}"
class="inline-flex rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
>
{{ __('Instellingen wijzigen (wizard)') }}
</a>
<form action="{{ route('admin.pages.weeztix.destroy', $page) }}" method="post" class="inline"
onsubmit="return confirm(@js(__('Weeztix-integratie voor deze pagina verwijderen? Opgeslagen tokens worden gewist.')));">
@csrf
@method('DELETE')
<button type="submit" class="rounded-lg border border-red-200 bg-white px-4 py-2.5 text-sm font-semibold text-red-700 shadow-sm hover:bg-red-50">
{{ __('Weeztix loskoppelen') }}
</button>
</form>
</div>
<div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8">
<h2 class="text-lg font-semibold text-slate-900">{{ __('Huidige configuratie') }}</h2>
<p class="mt-1 text-sm text-slate-600">{{ __('OAuth-gegevens zijn opgeslagen maar worden om veiligheidsredenen niet getoond.') }}</p>
<dl class="mt-6 space-y-4 border-t border-slate-100 pt-6 text-sm">
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Verbinding') }}</dt>
<dd>
@if ($wz->is_connected)
<span class="inline-flex rounded-full bg-emerald-100 px-2.5 py-0.5 text-xs font-medium text-emerald-800">{{ __('Verbonden') }}</span>
@else
<span class="inline-flex rounded-full bg-amber-100 px-2.5 py-0.5 text-xs font-medium text-amber-900">{{ __('Niet verbonden') }}</span>
@endif
</dd>
</div>
@if ($wz->is_connected && $wz->token_expires_at)
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Toegangstoken tot') }}</dt>
<dd class="font-mono text-xs text-slate-800">{{ $wz->token_expires_at->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</dd>
</div>
@endif
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Callback-URL (in Weeztix-dashboard)') }}</dt>
<dd class="break-all font-mono text-xs text-slate-600">{{ route('admin.weeztix.callback', absolute: true) }}</dd>
</div>
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Coupon') }}</dt>
<dd class="text-slate-800">{{ $wz->coupon_name ?: ($wz->coupon_guid ? $wz->coupon_guid : '—') }}</dd>
</div>
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Codevoorvoegsel') }}</dt>
<dd class="font-mono text-slate-800">{{ $wz->code_prefix ?? '—' }}</dd>
</div>
<div class="flex flex-col gap-1 sm:flex-row sm:justify-between">
<dt class="font-medium text-slate-700">{{ __('Gebruik per code') }}</dt>
<dd class="text-slate-800">{{ (int) ($wz->usage_count ?? 1) }}</dd>
</div>
</dl>
</div>
@else
{{-- Wizard --}}
<div class="mb-6 flex flex-wrap items-center justify-between gap-3">
@if ($wz !== null)
<a href="{{ route('admin.pages.weeztix.edit', ['page' => $page]) }}" class="text-sm font-medium text-slate-600 hover:text-slate-900">
{{ __('Annuleren en terug naar overzicht') }}
</a>
@endif
</div>
<div class="mb-8 flex flex-wrap items-center gap-2" aria-label="{{ __('Wizardstappen') }}">
@foreach ([1 => __('OAuth'), 2 => __('Verbinden'), 3 => __('Coupon')] as $num => $label)
@php
$active = $wizardStep === $num;
$done = $wizardStep > $num;
@endphp
<span class="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium
{{ $active ? 'border-indigo-500 bg-indigo-50 text-indigo-900' : '' }}
{{ $done ? 'border-emerald-200 bg-emerald-50 text-emerald-900' : '' }}
{{ ! $active && ! $done ? 'border-slate-200 bg-slate-50 text-slate-500' : '' }}">
<span class="tabular-nums">{{ $num }}</span>
{{ $label }}
</span>
@if ($num < 3)
<span class="text-slate-300" aria-hidden="true"></span>
@endif
@endforeach
</div>
<div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8 space-y-10">
@if ($wizardStep === 1)
<section class="space-y-4" aria-labelledby="wz-wizard-step1">
<h2 id="wz-wizard-step1" class="text-lg font-semibold text-slate-900">{{ __('Stap 1: OAuth-client') }}</h2>
@if ($hasStoredCredentials && ! $credentialsEdit)
<p class="text-sm text-slate-600">{{ __('Wil je Client ID, client secret of de callback-URI in Weeztix wijzigen? De callback-URL van deze applicatie is hieronder; die moet exact overeenkomen in het Weeztix-dashboard.') }}</p>
<p class="rounded-lg bg-slate-50 px-3 py-2 font-mono text-xs text-slate-800 break-all">{{ route('admin.weeztix.callback', absolute: true) }}</p>
<div class="flex flex-wrap gap-3 pt-2">
<a
href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 2]) }}"
class="inline-flex rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
>
{{ __('Nee, huidige gegevens behouden') }}
</a>
<a
href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 1, 'credentials' => 'edit']) }}"
class="inline-flex rounded-lg border border-slate-300 bg-white px-4 py-2.5 text-sm font-semibold text-slate-800 shadow-sm hover:bg-slate-50"
>
{{ __('Ja, Client ID en secret wijzigen') }}
</a>
</div>
@else
<p class="text-sm text-slate-600">{{ __('Vul de OAuth-client uit het Weeztix-dashboard in. Zet de redirect-URI exact op de onderstaande URL.') }}</p>
<p class="rounded-lg bg-slate-50 px-3 py-2 font-mono text-xs text-slate-800 break-all">{{ route('admin.weeztix.callback', absolute: true) }}</p>
<form method="post" action="{{ route('admin.pages.weeztix.update', $page) }}" class="space-y-4 pt-2">
@csrf
@method('PUT')
<input type="hidden" name="wizard" value="1">
<input type="hidden" name="wizard_credential_save" value="1">
<div>
<label for="weeztix_client_id" class="block text-sm font-medium text-slate-700">{{ __('Client ID') }}</label>
<input
id="weeztix_client_id"
name="client_id"
type="text"
autocomplete="off"
value="{{ old('client_id') }}"
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"
placeholder="{{ $wz !== null ? __('Laat leeg om ongewijzigd te laten') : '' }}"
@if ($wz === null) required @endif
>
</div>
<div>
<label for="weeztix_client_secret" class="block text-sm font-medium text-slate-700">{{ __('Client secret') }}</label>
<input
id="weeztix_client_secret"
name="client_secret"
type="password"
autocomplete="off"
value=""
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"
placeholder="{{ $wz !== null ? __('Laat leeg om ongewijzigd te laten') : '' }}"
@if ($wz === null) required @endif
>
</div>
@if ($wz !== null)
<p class="text-xs text-slate-500">
{{ __('Laat velden leeg om opgeslagen waarden te behouden.') }}
</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">
{{ __('Opslaan en verder naar verbinden') }}
</button>
</form>
@endif
</section>
@endif
@if ($wizardStep === 2)
<section class="space-y-4" aria-labelledby="wz-wizard-step2">
<h2 id="wz-wizard-step2" class="text-lg font-semibold text-slate-900">{{ __('Stap 2: Verbinden met Weeztix') }}</h2>
<p class="text-sm text-slate-600">{{ __('Log in bij Weeztix en keur de toegang goed. Daarna ga je automatisch verder naar de coupon.') }}</p>
<p class="rounded-lg bg-slate-50 px-3 py-2 font-mono text-xs text-slate-800 break-all">{{ route('admin.weeztix.callback', absolute: true) }}</p>
@if ($wz !== null && $wz->is_connected)
<div class="rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900">
{{ __('Je bent verbonden. Start opnieuw de Weeztix-login hieronder om een ander bedrijf te kiezen; het gekoppelde bedrijf wordt daarna automatisch bijgewerkt.') }}
</div>
@endif
<div class="flex flex-wrap items-center gap-3">
<a
href="{{ $oauthUrl }}"
class="inline-flex rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
>
@if ($wz !== null && $wz->is_connected)
{{ __('Opnieuw verbinden met Weeztix') }}
@else
{{ __('Verbind met Weeztix') }}
@endif
</a>
@if ($wz !== null && $wz->is_connected)
<a
href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 3]) }}"
class="inline-flex rounded-lg border border-slate-300 bg-white px-4 py-2.5 text-sm font-semibold text-slate-800 shadow-sm hover:bg-slate-50"
>
{{ __('Naar stap 3: coupon') }}
</a>
@endif
</div>
<p class="pt-4">
<a href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 1]) }}" class="text-sm font-medium text-slate-600 hover:text-slate-900">{{ __('← Terug naar stap 1') }}</a>
</p>
</section>
@endif
@if ($wizardStep === 3)
<div
x-data="weeztixSetup(@js([
'pageId' => $page->id,
'couponsUrl' => route('admin.weeztix.coupons'),
'csrf' => csrf_token(),
'isConnected' => $wz?->is_connected ?? false,
'callbackUrl' => route('admin.weeztix.callback', absolute: true),
'existing' => $existing,
'strings' => [
'genericError' => __('Er ging iets mis. Probeer het opnieuw.'),
'loadCouponsError' => __('Kon kortingsbonnen niet laden.'),
'refreshCoupons' => __('Vernieuwen'),
'refreshCouponsBusy' => __('Bezig…'),
'refreshCouponsTitle' => __('Couponlijst opnieuw ophalen van Weeztix'),
],
]))"
>
<div x-show="errorMessage !== ''" x-cloak class="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800" x-text="errorMessage"></div>
<section class="space-y-4" aria-labelledby="wz-wizard-step3">
<h2 id="wz-wizard-step3" class="text-lg font-semibold text-slate-900">{{ __('Stap 3: Coupon en codes') }}</h2>
<p class="text-sm text-slate-600">{{ __('Kies een bestaande coupon in Weeztix en stel het voorvoegsel en aantal gebruiken per code in.') }}</p>
<form method="post" action="{{ route('admin.pages.weeztix.update', $page) }}" class="space-y-4">
@csrf
@method('PUT')
<input type="hidden" name="wizard" value="1">
<input type="hidden" name="wizard_coupon_save" value="1">
<div>
<div class="flex flex-wrap items-end justify-between gap-2">
<label for="weeztix_coupon" class="block text-sm font-medium text-slate-700">{{ __('Coupon (kortingssjabloon)') }}</label>
<button
type="button"
x-show="isConnected"
x-cloak
@click="refreshCoupons()"
:disabled="couponsRefreshing"
:aria-busy="couponsRefreshing"
:title="strings.refreshCouponsTitle"
class="shrink-0 rounded-md border border-slate-300 bg-white px-2.5 py-1 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50"
>
<span x-show="!couponsRefreshing" x-text="strings.refreshCoupons"></span>
<span x-show="couponsRefreshing" x-cloak x-text="strings.refreshCouponsBusy"></span>
</button>
</div>
<select
id="weeztix_coupon"
name="coupon_guid"
x-model="couponGuid"
@change="syncCouponName()"
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 coupon…') }}</option>
<template x-for="c in coupons" :key="c.guid">
<option :value="c.guid" x-text="c.name"></option>
</template>
</select>
<input type="hidden" name="coupon_name" :value="couponName">
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label for="weeztix_code_prefix" class="block text-sm font-medium text-slate-700">{{ __('Codevoorvoegsel') }}</label>
<input
id="weeztix_code_prefix"
name="code_prefix"
type="text"
maxlength="32"
x-model="codePrefix"
value="{{ old('code_prefix', $wz->code_prefix ?? 'PREREG') }}"
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"
>
</div>
<div>
<label for="weeztix_usage_count" class="block text-sm font-medium text-slate-700">{{ __('Gebruik per code') }}</label>
<input
id="weeztix_usage_count"
name="usage_count"
type="number"
min="1"
max="99999"
x-model.number="usageCount"
value="{{ old('usage_count', $wz->usage_count ?? 1) }}"
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"
>
</div>
</div>
<div class="flex flex-wrap gap-3 pt-2">
<button type="submit" class="rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
{{ __('Opslaan en wizard afronden') }}
</button>
<a href="{{ route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 2]) }}" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 shadow-sm hover:bg-slate-50">
{{ __('Terug naar stap 2') }}
</a>
</div>
</form>
</section>
</div>
@endif
</div>
@endif
</div>
@endsection