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
This commit is contained in:
2026-04-05 11:12:10 +02:00
parent e0de8a05fa
commit 89931b817d
8 changed files with 504 additions and 210 deletions

View File

@@ -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
<div class="flex flex-wrap items-center gap-1.5 {{ $integrationBadgeClass }}">
@if ($showMailwizz)
<span
class="inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium {{ $mailwizzClasses }}"
title="{{ __('Mailwizz: :state', ['state' => $mailwizzLabel]) }}"
>
{{ __('MW') }} · {{ $mailwizzLabel }}
</span>
@endif
@if ($showWeeztix)
<span
class="inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium {{ $weeztixClasses }}"
title="{{ __('Weeztix: :state', ['state' => $weeztixLabel]) }}"
>
{{ __('WZ') }} · {{ $weeztixLabel }}
</span>
@endif
</div>

View File

@@ -13,10 +13,30 @@
{{ __('Public URL') }}: <a href="{{ route('public.page', ['publicPage' => $page]) }}" class="text-indigo-600 hover:underline" target="_blank" rel="noopener">{{ url('/r/'.$page->slug) }}</a>
</p>
@can('update', $page)
<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1">
<a href="{{ route('admin.pages.mailwizz.edit', $page) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-500">{{ __('Mailwizz integration') }} </a>
<a href="{{ route('admin.pages.weeztix.edit', $page) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-500">{{ __('Weeztix integration') }} </a>
</p>
<div class="mt-6 grid gap-4 sm:grid-cols-2">
<a
href="{{ route('admin.pages.mailwizz.edit', $page) }}"
class="group flex flex-col rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-indigo-200 hover:shadow-md"
>
<div class="flex items-start justify-between gap-3">
<h2 class="text-sm font-semibold text-slate-900">{{ __('Mailwizz') }}</h2>
@include('admin.pages._integration_badges', ['page' => $page, 'only' => 'mailwizz', 'integrationBadgeClass' => 'justify-end'])
</div>
<p class="mt-2 flex-1 text-xs text-slate-600">{{ __('Sync subscribers to your Mailwizz list and map fields.') }}</p>
<span class="mt-3 text-sm font-medium text-indigo-600 group-hover:text-indigo-500">{{ __('Open Mailwizz') }} </span>
</a>
<a
href="{{ route('admin.pages.weeztix.edit', $page) }}"
class="group flex flex-col rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-indigo-200 hover:shadow-md"
>
<div class="flex items-start justify-between gap-3">
<h2 class="text-sm font-semibold text-slate-900">{{ __('Weeztix') }}</h2>
@include('admin.pages._integration_badges', ['page' => $page, 'only' => 'weeztix', 'integrationBadgeClass' => 'justify-end'])
</div>
<p class="mt-2 flex-1 text-xs text-slate-600">{{ __('Issue unique discount codes via Weeztix when visitors sign up.') }}</p>
<span class="mt-3 text-sm font-medium text-indigo-600 group-hover:text-indigo-500">{{ __('Open Weeztix') }} </span>
</a>
</div>
@endcan
</div>

View File

@@ -32,6 +32,7 @@
<th scope="col" class="px-4 py-3 font-semibold text-slate-700">{{ __('Start') }}</th>
<th scope="col" class="px-4 py-3 font-semibold text-slate-700">{{ __('End') }}</th>
<th scope="col" class="px-4 py-3 font-semibold text-slate-700">{{ __('Subscribers') }}</th>
<th scope="col" class="px-4 py-3 font-semibold text-slate-700">{{ __('Integrations') }}</th>
<th scope="col" class="px-4 py-3 text-right font-semibold text-slate-700">{{ __('Actions') }}</th>
</tr>
</thead>
@@ -64,6 +65,9 @@
<td class="whitespace-nowrap px-4 py-3 text-slate-600">{{ $page->start_date->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</td>
<td class="whitespace-nowrap px-4 py-3 text-slate-600">{{ $page->end_date->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</td>
<td class="whitespace-nowrap px-4 py-3 tabular-nums text-slate-600">{{ number_format($page->subscribers_count) }}</td>
<td class="px-4 py-3">
@include('admin.pages._integration_badges', ['page' => $page])
</td>
<td class="whitespace-nowrap px-4 py-3 text-right">
<div class="flex flex-wrap items-center justify-end gap-2">
@can('update', $page)
@@ -77,9 +81,6 @@
@endcan
@can('update', $page)
<a href="{{ route('admin.pages.weeztix.edit', $page) }}" class="text-indigo-600 hover:text-indigo-500">{{ __('Weeztix') }}</a>
@if ($page->weeztixConfig?->is_connected)
<span class="text-xs font-medium text-emerald-600" title="{{ __('Weeztix verbonden') }}"></span>
@endif
@endcan
<button
type="button"
@@ -103,7 +104,7 @@
</tr>
@empty
<tr>
<td colspan="{{ auth()->user()->isSuperadmin() ? 7 : 6 }}" class="px-4 py-12 text-center text-slate-500">
<td colspan="{{ auth()->user()->isSuperadmin() ? 8 : 7 }}" class="px-4 py-12 text-center text-slate-500">
{{ __('No pages yet.') }}
@can('create', \App\Models\PreregistrationPage::class)
<a href="{{ route('admin.pages.create') }}" class="font-medium text-indigo-600 hover:text-indigo-500">{{ __('Create one') }}</a>