feat: Phase 3 - public registration pages and Mailwizz config
This commit is contained in:
@@ -13,34 +13,143 @@ class MailwizzApiController extends Controller
|
||||
{
|
||||
public function lists(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate(['api_key' => 'required|string']);
|
||||
$request->validate(['api_key' => ['required', 'string']]);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'X-Api-Key' => $request->api_key,
|
||||
'X-Api-Key' => $request->string('api_key')->toString(),
|
||||
])->get('https://www.mailwizz.nl/api/lists');
|
||||
|
||||
if ($response->failed()) {
|
||||
return response()->json(['error' => 'Invalid API key or connection failed'], 422);
|
||||
return response()->json(['message' => __('Invalid API key or connection failed.')], 422);
|
||||
}
|
||||
|
||||
return response()->json($response->json());
|
||||
$json = $response->json();
|
||||
if (is_array($json) && ($json['status'] ?? null) === 'error') {
|
||||
return response()->json(['message' => __('Invalid API key or connection failed.')], 422);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'lists' => $this->normalizeListsPayload(is_array($json) ? $json : []),
|
||||
]);
|
||||
}
|
||||
|
||||
public function fields(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'api_key' => 'required|string',
|
||||
'list_uid' => 'required|string',
|
||||
'api_key' => ['required', 'string'],
|
||||
'list_uid' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
$listUid = $request->string('list_uid')->toString();
|
||||
$response = Http::withHeaders([
|
||||
'X-Api-Key' => $request->api_key,
|
||||
])->get("https://www.mailwizz.nl/api/lists/{$request->list_uid}/fields");
|
||||
'X-Api-Key' => $request->string('api_key')->toString(),
|
||||
])->get("https://www.mailwizz.nl/api/lists/{$listUid}/fields");
|
||||
|
||||
if ($response->failed()) {
|
||||
return response()->json(['error' => 'Failed to fetch list fields'], 422);
|
||||
return response()->json(['message' => __('Failed to fetch list fields.')], 422);
|
||||
}
|
||||
|
||||
return response()->json($response->json());
|
||||
$json = $response->json();
|
||||
if (is_array($json) && ($json['status'] ?? null) === 'error') {
|
||||
return response()->json(['message' => __('Failed to fetch list fields.')], 422);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'fields' => $this->normalizeFieldsPayload(is_array($json) ? $json : []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{list_uid: string, name: string}>
|
||||
*/
|
||||
private function normalizeListsPayload(array $json): array
|
||||
{
|
||||
$out = [];
|
||||
$records = data_get($json, 'data.records');
|
||||
if (! is_array($records)) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
foreach ($records as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$uid = data_get($row, 'general.list_uid') ?? data_get($row, 'list_uid');
|
||||
$name = data_get($row, 'general.name') ?? data_get($row, 'name');
|
||||
if (is_string($uid) && $uid !== '' && is_string($name) && $name !== '') {
|
||||
$out[] = ['list_uid' => $uid, 'name' => $name];
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{tag: string, label: string, type_identifier: string, options: array<string, string>}>
|
||||
*/
|
||||
private function normalizeFieldsPayload(array $json): array
|
||||
{
|
||||
$out = [];
|
||||
$records = data_get($json, 'data.records');
|
||||
if (! is_array($records)) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
foreach ($records as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$tag = data_get($row, 'tag');
|
||||
$label = data_get($row, 'label');
|
||||
$typeId = data_get($row, 'type.identifier');
|
||||
if (! is_string($tag) || $tag === '' || ! is_string($label)) {
|
||||
continue;
|
||||
}
|
||||
$typeIdentifier = is_string($typeId) ? $typeId : '';
|
||||
$rawOptions = data_get($row, 'options');
|
||||
$options = $this->normalizeFieldOptions($rawOptions);
|
||||
|
||||
$out[] = [
|
||||
'tag' => $tag,
|
||||
'label' => $label,
|
||||
'type_identifier' => $typeIdentifier,
|
||||
'options' => $options,
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function normalizeFieldOptions(mixed $rawOptions): array
|
||||
{
|
||||
if ($rawOptions === null || $rawOptions === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (is_string($rawOptions)) {
|
||||
$decoded = json_decode($rawOptions, true);
|
||||
if (is_array($decoded)) {
|
||||
$rawOptions = $decoded;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_array($rawOptions)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($rawOptions as $key => $value) {
|
||||
if (is_string($key) || is_int($key)) {
|
||||
$k = (string) $key;
|
||||
$out[$k] = is_scalar($value) ? (string) $value : '';
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,51 @@ declare(strict_types=1);
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\UpdateMailwizzConfigRequest;
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class MailwizzController extends Controller
|
||||
{
|
||||
public function edit(PreregistrationPage $page): \Illuminate\View\View
|
||||
public function edit(PreregistrationPage $page): View
|
||||
{
|
||||
$this->authorize('update', $page);
|
||||
|
||||
$page->load('mailwizzConfig');
|
||||
|
||||
return view('admin.mailwizz.edit', compact('page'));
|
||||
}
|
||||
|
||||
public function update(Request $request, PreregistrationPage $page): \Illuminate\Http\RedirectResponse
|
||||
public function update(UpdateMailwizzConfigRequest $request, PreregistrationPage $page): RedirectResponse
|
||||
{
|
||||
return redirect()->route('admin.pages.mailwizz.edit', $page);
|
||||
$validated = $request->validated();
|
||||
|
||||
if (($validated['api_key'] ?? '') === '' && $page->mailwizzConfig !== null) {
|
||||
unset($validated['api_key']);
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($page, $validated): void {
|
||||
$page->mailwizzConfig()->updateOrCreate(
|
||||
['preregistration_page_id' => $page->id],
|
||||
array_merge($validated, ['preregistration_page_id' => $page->id])
|
||||
);
|
||||
});
|
||||
|
||||
return redirect()
|
||||
->route('admin.pages.mailwizz.edit', $page)
|
||||
->with('status', __('Mailwizz configuration saved.'));
|
||||
}
|
||||
|
||||
public function destroy(PreregistrationPage $page): \Illuminate\Http\RedirectResponse
|
||||
public function destroy(PreregistrationPage $page): RedirectResponse
|
||||
{
|
||||
return redirect()->route('admin.pages.mailwizz.edit', $page);
|
||||
$this->authorize('update', $page);
|
||||
|
||||
$page->mailwizzConfig()?->delete();
|
||||
|
||||
return redirect()
|
||||
->route('admin.pages.mailwizz.edit', $page)
|
||||
->with('status', __('Mailwizz integration removed.'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,55 +4,45 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\SubscribePublicPageRequest;
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PublicPageController extends Controller
|
||||
{
|
||||
public function show(string $slug): View
|
||||
public function show(PreregistrationPage $publicPage): View
|
||||
{
|
||||
$page = PreregistrationPage::where('slug', $slug)
|
||||
->where('is_active', true)
|
||||
->firstOrFail();
|
||||
|
||||
return view('public.page', compact('page'));
|
||||
return view('public.page', ['page' => $publicPage]);
|
||||
}
|
||||
|
||||
public function subscribe(Request $request, string $slug): JsonResponse
|
||||
public function subscribe(SubscribePublicPageRequest $request, PreregistrationPage $publicPage): JsonResponse
|
||||
{
|
||||
$page = PreregistrationPage::where('slug', $slug)
|
||||
->where('is_active', true)
|
||||
->firstOrFail();
|
||||
abort_if(now()->lt($publicPage->start_date) || now()->gt($publicPage->end_date), 403);
|
||||
|
||||
abort_if(now()->lt($page->start_date) || now()->gt($page->end_date), 403);
|
||||
$validated = $request->validated();
|
||||
|
||||
$validated = $request->validate([
|
||||
'first_name' => 'required|string|max:255',
|
||||
'last_name' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255',
|
||||
'phone' => $page->phone_enabled ? 'required|string|max:20' : 'nullable',
|
||||
]);
|
||||
$exists = $publicPage->subscribers()
|
||||
->where('email', $validated['email'])
|
||||
->exists();
|
||||
|
||||
$exists = $page->subscribers()->where('email', $validated['email'])->exists();
|
||||
if ($exists) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'This email address is already registered.',
|
||||
'message' => __('You are already registered for this event.'),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$subscriber = $page->subscribers()->create($validated);
|
||||
$publicPage->subscribers()->create($validated);
|
||||
|
||||
// Mailwizz sync will be wired up in Phase 4
|
||||
if ($page->mailwizzConfig) {
|
||||
if ($publicPage->mailwizzConfig) {
|
||||
// SyncSubscriberToMailwizz::dispatch($subscriber);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $page->thank_you_message ?? 'Thank you for registering!',
|
||||
'message' => $publicPage->thank_you_message ?? __('Thank you for registering!'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
51
app/Http/Requests/Admin/UpdateMailwizzConfigRequest.php
Normal file
51
app/Http/Requests/Admin/UpdateMailwizzConfigRequest.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateMailwizzConfigRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
$page = $this->route('page');
|
||||
if (! $page instanceof PreregistrationPage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->user()?->can('update', $page) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int, ValidationRule|string>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
/** @var PreregistrationPage $page */
|
||||
$page = $this->route('page');
|
||||
|
||||
return [
|
||||
'api_key' => [
|
||||
Rule::requiredIf(fn (): bool => $page->mailwizzConfig === null),
|
||||
'nullable',
|
||||
'string',
|
||||
'max:512',
|
||||
],
|
||||
'list_uid' => ['required', 'string', 'max:255'],
|
||||
'list_name' => ['nullable', 'string', 'max:255'],
|
||||
'field_email' => ['required', 'string', 'max:255'],
|
||||
'field_first_name' => ['required', 'string', 'max:255'],
|
||||
'field_last_name' => ['required', 'string', 'max:255'],
|
||||
'field_phone' => $page->phone_enabled
|
||||
? ['required', 'string', 'max:255']
|
||||
: ['nullable', 'string', 'max:255'],
|
||||
'tag_field' => ['required', 'string', 'max:255'],
|
||||
'tag_value' => ['required', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
}
|
||||
44
app/Http/Requests/SubscribePublicPageRequest.php
Normal file
44
app/Http/Requests/SubscribePublicPageRequest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SubscribePublicPageRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
/** @var PreregistrationPage $page */
|
||||
$page = $this->route('publicPage');
|
||||
|
||||
return [
|
||||
'first_name' => ['required', 'string', 'max:255'],
|
||||
'last_name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'phone' => $page->phone_enabled
|
||||
? ['required', 'string', 'max:20']
|
||||
: ['nullable', 'string', 'max:20'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$email = $this->input('email');
|
||||
if (is_string($email)) {
|
||||
$this->merge([
|
||||
'email' => strtolower(trim($email)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -23,5 +25,12 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
Paginator::useTailwind();
|
||||
|
||||
Route::bind('publicPage', function (string $value): PreregistrationPage {
|
||||
return PreregistrationPage::query()
|
||||
->where('slug', $value)
|
||||
->where('is_active', true)
|
||||
->firstOrFail();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user