Files
preregister/resources/views/admin/subscribers/index.blade.php
bert.hausmans d3abdb7ed9 feat: add Weeztix OAuth, coupon codes, and Mailwizz mapping
Implement Weeztix integration per documentation: database config and
subscriber coupon_code, OAuth redirect/callback, admin setup UI with
company/coupon selection via AJAX, synchronous coupon creation on public
subscribe with duplicate and rate-limit handling, Mailwizz field mapping
for coupon codes, subscriber table and CSV export, and connection hint
on the pages list.

Made-with: Cursor
2026-04-04 14:52:41 +02:00

121 lines
8.3 KiB
PHP

@extends('layouts.admin')
@section('title', __('Subscribers') . ' — ' . $page->title)
@section('mobile_title', __('Subscribers'))
@section('content')
<div class="mx-auto max-w-7xl">
<div class="mb-8">
<a href="{{ route('admin.pages.index') }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-500"> {{ __('Back to pages') }}</a>
<h1 class="mt-4 text-2xl font-semibold text-slate-900">{{ __('Subscribers') }}</h1>
<p class="mt-1 text-sm text-slate-600">{{ $page->title }}</p>
</div>
<div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<form method="get" action="{{ route('admin.pages.subscribers.index', $page) }}" class="flex flex-1 flex-col gap-2 sm:max-w-md">
<label for="search" class="text-sm font-medium text-slate-700">{{ __('Search by name or email') }}</label>
<div class="flex gap-2">
<input type="search" name="search" id="search" value="{{ request('search') }}"
class="block w-full rounded-lg border-slate-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" placeholder="{{ __('Search…') }}" />
<button type="submit" class="rounded-lg bg-slate-800 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-700">{{ __('Filter') }}</button>
</div>
@error('search')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</form>
<div class="flex flex-wrap items-center gap-2">
@if ($unsyncedMailwizzCount > 0)
<form method="post" action="{{ route('admin.pages.subscribers.queue-mailwizz-sync', $page) }}" class="inline"
onsubmit="return confirm(@js(__('Queue Mailwizz sync jobs for all :count not-yet-synced subscribers on this page? Duplicates are skipped if a job is already pending.', ['count' => $unsyncedMailwizzCount])));">
@csrf
<button type="submit" class="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">
{{ __('Queue Mailwizz sync') }} ({{ $unsyncedMailwizzCount }})
</button>
</form>
@endif
<a href="{{ route('admin.pages.subscribers.export', $page) }}{{ request()->filled('search') ? '?'.http_build_query(['search' => request('search')]) : '' }}"
class="inline-flex items-center justify-center rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm hover:bg-slate-50">
{{ __('Export CSV') }}
</a>
</div>
</div>
<div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-left text-sm">
<thead class="bg-slate-50">
<tr>
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('First name') }}</th>
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Last name') }}</th>
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Email') }}</th>
@if ($page->isPhoneFieldEnabledForSubscribers())
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Phone') }}</th>
@endif
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Kortingscode') }}</th>
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Registered at') }}</th>
<th class="px-4 py-3 font-semibold text-slate-700">{{ __('Mailwizz') }}</th>
<th class="w-px whitespace-nowrap px-4 py-3 font-semibold text-slate-700">{{ __('Actions') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@forelse ($subscribers as $subscriber)
<tr class="hover:bg-slate-50/80">
<td class="px-4 py-3 text-slate-900">{{ $subscriber->first_name }}</td>
<td class="px-4 py-3 text-slate-900">{{ $subscriber->last_name }}</td>
<td class="px-4 py-3 text-slate-600">{{ $subscriber->email }}</td>
@if ($page->isPhoneFieldEnabledForSubscribers())
<td class="px-4 py-3 text-slate-600">{{ $subscriber->phoneDisplay() ?? '—' }}</td>
@endif
<td class="px-4 py-3 font-mono text-xs text-slate-700">{{ $subscriber->coupon_code ?? '—' }}</td>
<td class="whitespace-nowrap px-4 py-3 text-slate-600">{{ $subscriber->created_at->timezone(config('app.timezone'))->format('Y-m-d H:i') }}</td>
<td class="px-4 py-3">
@if ($subscriber->synced_to_mailwizz)
<span class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-emerald-100 text-emerald-700" title="{{ __('Synced') }}">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
</span>
@else
<span class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 text-slate-500" title="{{ __('Not synced') }}">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
</span>
@endif
</td>
<td class="whitespace-nowrap px-4 py-3 text-right">
@can('update', $page)
<form
method="post"
action="{{ route('admin.pages.subscribers.destroy', [$page, $subscriber]) }}"
class="inline"
onsubmit="return confirm(@js(__('Delete this subscriber? This cannot be undone.')));"
>
@csrf
@method('DELETE')
<button
type="submit"
class="rounded-lg border border-red-200 bg-white px-2.5 py-1 text-xs font-semibold text-red-700 hover:bg-red-50"
>
{{ __('Remove') }}
</button>
</form>
@endcan
</td>
</tr>
@empty
<tr>
<td colspan="{{ $page->isPhoneFieldEnabledForSubscribers() ? 8 : 7 }}" class="px-4 py-12 text-center text-slate-500">
{{ __('No subscribers match your criteria.') }}
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if ($subscribers->hasPages())
<div class="border-t border-slate-200 px-4 py-3">
{{ $subscribers->links() }}
</div>
@endif
</div>
</div>
@endsection