From 89931b817d7509bd8e47de4187d774424f95b363 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Sun, 5 Apr 2026 11:12:10 +0200 Subject: [PATCH] feat(admin): Weeztix setup wizard, integration status badges - Summary view when Weeztix is configured; edits only via 3-step wizard - Step 1: reuse or replace OAuth client ID/secret; callback URL shown - Step 2: OAuth connect (resume wizard after callback when started from wizard) - Step 3: coupon, prefix, usage; finishing exits wizard - PreregistrationPage: mailwizz/weeztix integration status helpers - Pages index: Integrations column with MW/WZ badges; edit page: status cards Made-with: Cursor --- app/Http/Controllers/Admin/PageController.php | 8 +- .../Controllers/Admin/WeeztixController.php | 40 +- .../Admin/WeeztixOAuthController.php | 39 +- app/Models/PreregistrationPage.php | 52 ++ .../admin/pages/_integration_badges.blade.php | 55 ++ resources/views/admin/pages/edit.blade.php | 28 +- resources/views/admin/pages/index.blade.php | 9 +- resources/views/admin/weeztix/edit.blade.php | 483 +++++++++++------- 8 files changed, 504 insertions(+), 210 deletions(-) create mode 100644 resources/views/admin/pages/_integration_badges.blade.php diff --git a/app/Http/Controllers/Admin/PageController.php b/app/Http/Controllers/Admin/PageController.php index 6835d33..986b30e 100644 --- a/app/Http/Controllers/Admin/PageController.php +++ b/app/Http/Controllers/Admin/PageController.php @@ -28,7 +28,7 @@ class PageController extends Controller { $query = PreregistrationPage::query() ->withCount('subscribers') - ->with('weeztixConfig') + ->with(['weeztixConfig', 'mailwizzConfig']) ->orderByDesc('start_date'); if (! $request->user()?->isSuperadmin()) { @@ -86,7 +86,11 @@ class PageController extends Controller public function edit(PreregistrationPage $page): View { - $page->load(['blocks' => fn ($q) => $q->orderBy('sort_order')]); + $page->load([ + 'blocks' => fn ($q) => $q->orderBy('sort_order'), + 'mailwizzConfig', + 'weeztixConfig', + ]); return view('admin.pages.edit', compact('page')); } diff --git a/app/Http/Controllers/Admin/WeeztixController.php b/app/Http/Controllers/Admin/WeeztixController.php index c85a2f8..444041d 100644 --- a/app/Http/Controllers/Admin/WeeztixController.php +++ b/app/Http/Controllers/Admin/WeeztixController.php @@ -8,18 +8,34 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Admin\UpdateWeeztixConfigRequest; use App\Models\PreregistrationPage; use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\View\View; class WeeztixController extends Controller { - public function edit(PreregistrationPage $page): View + public function edit(Request $request, PreregistrationPage $page): View { $this->authorize('update', $page); $page->load('weeztixConfig'); - return view('admin.weeztix.edit', compact('page')); + $showWizard = $page->weeztixConfig === null || $request->boolean('wizard'); + $wizardStep = $showWizard ? min(3, max(1, (int) $request->query('step', 1))) : 1; + if ($showWizard && $page->weeztixConfig === null && $wizardStep !== 1) { + return redirect() + ->route('admin.pages.weeztix.edit', ['page' => $page, 'wizard' => 1, 'step' => 1]); + } + $hasStoredCredentials = $page->weeztixConfig !== null + && is_string($page->weeztixConfig->client_id) + && $page->weeztixConfig->client_id !== ''; + + return view('admin.weeztix.edit', compact( + 'page', + 'showWizard', + 'wizardStep', + 'hasStoredCredentials' + )); } public function update(UpdateWeeztixConfigRequest $request, PreregistrationPage $page): RedirectResponse @@ -41,8 +57,26 @@ class WeeztixController extends Controller ); }); + $page->load('weeztixConfig'); + + if ($request->boolean('wizard')) { + if ($request->boolean('wizard_coupon_save')) { + return redirect() + ->route('admin.pages.weeztix.edit', ['page' => $page]) + ->with('status', __('Weeztix-configuratie opgeslagen.')); + } + + return redirect() + ->route('admin.pages.weeztix.edit', [ + 'page' => $page, + 'wizard' => 1, + 'step' => 2, + ]) + ->with('status', __('Weeztix-configuratie opgeslagen.')); + } + return redirect() - ->route('admin.pages.weeztix.edit', $page) + ->route('admin.pages.weeztix.edit', ['page' => $page]) ->with('status', __('Weeztix-configuratie opgeslagen.')); } diff --git a/app/Http/Controllers/Admin/WeeztixOAuthController.php b/app/Http/Controllers/Admin/WeeztixOAuthController.php index bd19ad5..8466723 100644 --- a/app/Http/Controllers/Admin/WeeztixOAuthController.php +++ b/app/Http/Controllers/Admin/WeeztixOAuthController.php @@ -15,7 +15,7 @@ use RuntimeException; class WeeztixOAuthController extends Controller { - public function redirect(PreregistrationPage $page): RedirectResponse + public function redirect(Request $request, PreregistrationPage $page): RedirectResponse { $this->authorize('update', $page); @@ -38,6 +38,7 @@ class WeeztixOAuthController extends Controller session([ 'weeztix_oauth_state' => $state, 'weeztix_page_id' => $page->id, + 'weeztix_oauth_resume_wizard' => $request->boolean('wizard'), ]); $redirectUri = $config->redirect_uri; @@ -90,7 +91,7 @@ class WeeztixOAuthController extends Controller $config = $page->weeztixConfig; if ($config === null) { - session()->forget(['weeztix_oauth_state', 'weeztix_page_id']); + $this->forgetOauthSession(); return redirect() ->route('admin.pages.weeztix.edit', $page) @@ -110,30 +111,52 @@ class WeeztixOAuthController extends Controller 'message' => $e->getMessage(), ]); - session()->forget(['weeztix_oauth_state', 'weeztix_page_id']); + $resumeWizard = $this->forgetOauthSession(); return redirect() - ->route('admin.pages.weeztix.edit', $page) + ->route('admin.pages.weeztix.edit', $this->weeztixEditParams($page, $resumeWizard, 2)) ->with('error', __('Verbinden met Weeztix is mislukt. Controleer je gegevens en probeer opnieuw.')); } - session()->forget(['weeztix_oauth_state', 'weeztix_page_id']); + $resumeWizard = $this->forgetOauthSession(); return redirect() - ->route('admin.pages.weeztix.edit', $page) + ->route('admin.pages.weeztix.edit', $this->weeztixEditParams($page, $resumeWizard, 3)) ->with('status', __('Succesvol verbonden met Weeztix.')); } + /** + * @return array{page: PreregistrationPage, wizard?: int, step?: int} + */ + private function weeztixEditParams(PreregistrationPage $page, bool $resumeWizard, int $step): array + { + $params = ['page' => $page]; + if ($resumeWizard) { + $params['wizard'] = 1; + $params['step'] = $step; + } + + return $params; + } + + private function forgetOauthSession(): bool + { + $resumeWizard = (bool) session()->pull('weeztix_oauth_resume_wizard', false); + session()->forget(['weeztix_oauth_state', 'weeztix_page_id']); + + return $resumeWizard; + } + private function redirectToWeeztixEditWithSessionPage(string $message): RedirectResponse { $pageId = session('weeztix_page_id'); - session()->forget(['weeztix_oauth_state', 'weeztix_page_id']); + $resumeWizard = $this->forgetOauthSession(); if (is_int($pageId) || is_numeric($pageId)) { $page = PreregistrationPage::query()->find((int) $pageId); if ($page !== null) { return redirect() - ->route('admin.pages.weeztix.edit', $page) + ->route('admin.pages.weeztix.edit', $this->weeztixEditParams($page, $resumeWizard, 2)) ->with('error', $message); } } diff --git a/app/Models/PreregistrationPage.php b/app/Models/PreregistrationPage.php index 73ca190..98884f7 100644 --- a/app/Models/PreregistrationPage.php +++ b/app/Models/PreregistrationPage.php @@ -204,4 +204,56 @@ class PreregistrationPage extends Model return 'active'; } + + /** + * Mailwizz setup depth for admin UI (API key + list + email field = ready to sync). + * + * @return 'none'|'partial'|'ready' + */ + public function mailwizzIntegrationStatus(): string + { + $c = $this->mailwizzConfig; + if ($c === null) { + return 'none'; + } + + $key = $c->api_key; + if (! is_string($key) || $key === '') { + return 'partial'; + } + + if (! is_string($c->list_uid) || $c->list_uid === '' || ! is_string($c->field_email) || $c->field_email === '') { + return 'partial'; + } + + return 'ready'; + } + + /** + * Weeztix setup depth for admin UI. + * + * @return 'none'|'credentials'|'connected'|'ready' + */ + public function weeztixIntegrationStatus(): string + { + $c = $this->weeztixConfig; + if ($c === null) { + return 'none'; + } + + $hasClient = is_string($c->client_id) && $c->client_id !== ''; + if (! $hasClient) { + return 'none'; + } + + if (! $c->is_connected) { + return 'credentials'; + } + + if (! is_string($c->coupon_guid) || $c->coupon_guid === '') { + return 'connected'; + } + + return 'ready'; + } } diff --git a/resources/views/admin/pages/_integration_badges.blade.php b/resources/views/admin/pages/_integration_badges.blade.php new file mode 100644 index 0000000..faaad8f --- /dev/null +++ b/resources/views/admin/pages/_integration_badges.blade.php @@ -0,0 +1,55 @@ +@php + $only = $only ?? null; + $integrationBadgeClass = $integrationBadgeClass ?? ''; + if (! in_array($only, [null, 'mailwizz', 'weeztix'], true)) { + $only = null; + } + + $mailwizz = $page->mailwizzIntegrationStatus(); + $weeztix = $page->weeztixIntegrationStatus(); + + $mailwizzClasses = match ($mailwizz) { + 'ready' => 'border-emerald-200 bg-emerald-50 text-emerald-900', + 'partial' => 'border-amber-200 bg-amber-50 text-amber-950', + default => 'border-slate-200 bg-slate-50 text-slate-600', + }; + $mailwizzLabel = match ($mailwizz) { + 'ready' => __('Ready'), + 'partial' => __('Incomplete'), + default => __('Off'), + }; + + $weeztixClasses = match ($weeztix) { + 'ready' => 'border-emerald-200 bg-emerald-50 text-emerald-900', + 'connected' => 'border-sky-200 bg-sky-50 text-sky-950', + 'credentials' => 'border-amber-200 bg-amber-50 text-amber-950', + default => 'border-slate-200 bg-slate-50 text-slate-600', + }; + $weeztixLabel = match ($weeztix) { + 'ready' => __('Ready'), + 'connected' => __('Connected'), + 'credentials' => __('OAuth only'), + default => __('Off'), + }; + + $showMailwizz = $only === null || $only === 'mailwizz'; + $showWeeztix = $only === null || $only === 'weeztix'; +@endphp +
+ @if ($showMailwizz) + + {{ __('MW') }} · {{ $mailwizzLabel }} + + @endif + @if ($showWeeztix) + + {{ __('WZ') }} · {{ $weeztixLabel }} + + @endif +
diff --git a/resources/views/admin/pages/edit.blade.php b/resources/views/admin/pages/edit.blade.php index 6cdeb97..697ce75 100644 --- a/resources/views/admin/pages/edit.blade.php +++ b/resources/views/admin/pages/edit.blade.php @@ -13,10 +13,30 @@ {{ __('Public URL') }}: {{ url('/r/'.$page->slug) }}

@can('update', $page) -

- {{ __('Mailwizz integration') }} → - {{ __('Weeztix integration') }} → -

+
+ +
+

{{ __('Mailwizz') }}

+ @include('admin.pages._integration_badges', ['page' => $page, 'only' => 'mailwizz', 'integrationBadgeClass' => 'justify-end']) +
+

{{ __('Sync subscribers to your Mailwizz list and map fields.') }}

+ {{ __('Open Mailwizz') }} → +
+ +
+

{{ __('Weeztix') }}

+ @include('admin.pages._integration_badges', ['page' => $page, 'only' => 'weeztix', 'integrationBadgeClass' => 'justify-end']) +
+

{{ __('Issue unique discount codes via Weeztix when visitors sign up.') }}

+ {{ __('Open Weeztix') }} → +
+
@endcan diff --git a/resources/views/admin/pages/index.blade.php b/resources/views/admin/pages/index.blade.php index 394f515..51b8e65 100644 --- a/resources/views/admin/pages/index.blade.php +++ b/resources/views/admin/pages/index.blade.php @@ -32,6 +32,7 @@ {{ __('Start') }} {{ __('End') }} {{ __('Subscribers') }} + {{ __('Integrations') }} {{ __('Actions') }} @@ -64,6 +65,9 @@ {{ $page->start_date->timezone(config('app.timezone'))->format('Y-m-d H:i') }} {{ $page->end_date->timezone(config('app.timezone'))->format('Y-m-d H:i') }} {{ number_format($page->subscribers_count) }} + + @include('admin.pages._integration_badges', ['page' => $page]) +
@can('update', $page) @@ -77,9 +81,6 @@ @endcan @can('update', $page) {{ __('Weeztix') }} - @if ($page->weeztixConfig?->is_connected) - - @endif @endcan - - @endif - -
-
- -
-

{{ __('Stap 1: OAuth-gegevens') }}

-

- {{ __('Maak eerst een OAuth-client in het Weeztix-dashboard en stel de redirect-URI exact in op:') }} -

-

-

- {{ __('Maak daarna een korting (coupon) in Weeztix; die kies je hierna in stap 2.') }} -

- -
+
+ + {{ __('Instellingen wijzigen (wizard)') }} + + @csrf - @method('PUT') -
- - -
-
- - -
- @if ($wz !== null) -

- {{ __('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.') }} -

- @endif - +
+
+

{{ __('Huidige configuratie') }}

+

{{ __('OAuth-gegevens zijn opgeslagen maar worden om veiligheidsredenen niet getoond.') }}

+ +
+
+
{{ __('Verbinding') }}
+
+ @if ($wz->is_connected) + {{ __('Verbonden') }} + @else + {{ __('Niet verbonden') }} + @endif +
+
+ @if ($wz->is_connected && $wz->token_expires_at) +
+
{{ __('Toegangstoken tot') }}
+
{{ $wz->token_expires_at->timezone(config('app.timezone'))->format('Y-m-d H:i') }}
+
+ @endif +
+
{{ __('Callback-URL (in Weeztix-dashboard)') }}
+
{{ route('admin.weeztix.callback', absolute: true) }}
+
+
+
{{ __('Coupon') }}
+
{{ $wz->coupon_name ?: ($wz->coupon_guid ? $wz->coupon_guid : '—') }}
+
+
+
{{ __('Codevoorvoegsel') }}
+
{{ $wz->code_prefix ?? '—' }}
+
+
+
{{ __('Gebruik per code') }}
+
{{ (int) ($wz->usage_count ?? 1) }}
+
+
+
+ @else + {{-- Wizard --}} +
@if ($wz !== null) - + +
+ @foreach ([1 => __('OAuth'), 2 => __('Verbinden'), 3 => __('Coupon')] as $num => $label) + @php + $active = $wizardStep === $num; + $done = $wizardStep > $num; + @endphp + + {{ $num }} + {{ $label }} + + @if ($num < 3) + + @endif + @endforeach +
+ +
+ @if ($wizardStep === 1) +
+

{{ __('Stap 1: OAuth-client') }}

+ + @if ($hasStoredCredentials && ! $credentialsEdit) +

{{ __('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.') }}

+

{{ route('admin.weeztix.callback', absolute: true) }}

+ + @else +

{{ __('Vul de OAuth-client uit het Weeztix-dashboard in. Zet de redirect-URI exact op de onderstaande URL.') }}

+

{{ route('admin.weeztix.callback', absolute: true) }}

+ +
+ @csrf + @method('PUT') + + +
+ + +
+
+ + +
+ @if ($wz !== null) +

+ {{ __('Laat velden leeg om opgeslagen waarden te behouden.') }} +

+ @endif + +
+ @endif +
+ @endif + + @if ($wizardStep === 2) +
+

{{ __('Stap 2: Verbinden met Weeztix') }}

+

{{ __('Log in bij Weeztix en keur de toegang goed. Daarna ga je automatisch verder naar de coupon.') }}

+

{{ route('admin.weeztix.callback', absolute: true) }}

+ + @if ($wz !== null && $wz->is_connected) +
+ {{ __('Je bent al verbonden. Ga door naar de coupon.') }} +
+ + {{ __('Naar stap 3: coupon') }} + + @else + + {{ __('Verbind met Weeztix') }} + + @endif + +

+ {{ __('← Terug naar stap 1') }} +

+
+ @endif + + @if ($wizardStep === 3) +
+
+ +
+

{{ __('Stap 3: Coupon en codes') }}

+

{{ __('Kies een bestaande coupon in Weeztix en stel het voorvoegsel en aantal gebruiken per code in.') }}

+ +
+ @csrf + @method('PUT') + + + +
+
+ + +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + + {{ __('Terug naar stap 2') }} + +
+
+
@endif -
- - @if ($wz !== null) -
-

{{ __('Stap 2: Kortingsbon') }}

-

{{ __('Na een geslaagde verbinding kies je een bestaande coupon uit Weeztix. Het bedrijf volgt uit je Weeztix-login en wordt automatisch opgeslagen.') }}

- -
- @csrf - @method('PUT') - -
-
- - -
- - -
- -
-
- - -
-
- - -
-
- - -
-
- @endif -
+
+ @endif @endsection