feat: Phase 5 - polish, validation, rate limiting, Dutch translations

This commit is contained in:
2026-04-03 22:13:59 +02:00
parent 83e2158383
commit 330950cc6e
18 changed files with 251 additions and 102 deletions

View File

@@ -45,6 +45,17 @@
<p class="mt-2 text-sm text-slate-600">{{ __('Page:') }} <span class="font-medium text-slate-800">{{ $page->title }}</span></p>
</div>
@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">{{ __('Please fix the following:') }}</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 ($config !== null)
<div class="mb-8 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900">
<p class="font-medium">{{ __('Integration active') }}</p>

View File

@@ -13,6 +13,47 @@
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans antialiased bg-slate-100 text-slate-900" x-data="{ sidebarOpen: false }">
@php
$adminFlashSuccess = session('status');
$adminFlashError = session('error');
@endphp
@if ($adminFlashSuccess !== null || $adminFlashError !== null)
<div
class="pointer-events-none fixed bottom-4 right-4 z-[100] flex w-full max-w-sm flex-col gap-2 px-4 sm:px-0"
aria-live="polite"
>
@foreach (array_filter([
$adminFlashSuccess !== null ? ['type' => 'success', 'message' => $adminFlashSuccess] : null,
$adminFlashError !== null ? ['type' => 'error', 'message' => $adminFlashError] : null,
]) as $toast)
<div
x-data="{ visible: true }"
x-show="visible"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="translate-y-2 opacity-0"
x-transition:enter-end="translate-y-0 opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="translate-y-0 opacity-100"
x-transition:leave-end="translate-y-2 opacity-0"
x-init="setTimeout(() => visible = false, 6500)"
class="pointer-events-auto flex items-start gap-3 rounded-lg border px-4 py-3 text-sm shadow-lg {{ $toast['type'] === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-900' : 'border-red-200 bg-red-50 text-red-900' }}"
role="{{ $toast['type'] === 'success' ? 'status' : 'alert' }}"
>
<p class="min-w-0 flex-1 leading-snug">{{ $toast['message'] }}</p>
<button
type="button"
class="shrink-0 rounded p-0.5 opacity-70 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-indigo-500"
@click="visible = false"
aria-label="{{ __('Dismiss notification') }}"
>
<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>
</button>
</div>
@endforeach
</div>
@endif
{{-- Mobile overlay --}}
<div
x-show="sidebarOpen"
@@ -114,18 +155,6 @@
</header>
<main class="flex-1 p-4 sm:p-6 lg:p-8">
@if (session('status'))
<div class="mb-6 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800" role="status">
{{ session('status') }}
</div>
@endif
@if (session('error'))
<div class="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800" role="alert">
{{ session('error') }}
</div>
@endif
@yield('content')
</main>
</div>

View File

@@ -1,29 +1,27 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Profile') }}
</h2>
</x-slot>
@extends('layouts.admin')
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-profile-information-form')
</div>
</div>
@section('title', __('Profile'))
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-password-form')
</div>
</div>
@section('mobile_title', __('Profile'))
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.delete-user-form')
</div>
</div>
@section('content')
<div class="mx-auto max-w-3xl space-y-6">
<div>
<a href="{{ route('admin.dashboard') }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-500"> {{ __('Back to dashboard') }}</a>
<h1 class="mt-4 text-2xl font-semibold text-slate-900">{{ __('Profile') }}</h1>
<p class="mt-1 text-sm text-slate-600">{{ __('Update your account settings and password.') }}</p>
</div>
<div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8">
@include('profile.partials.update-profile-information-form')
</div>
<div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8">
@include('profile.partials.update-password-form')
</div>
<div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8">
@include('profile.partials.delete-user-form')
</div>
</div>
</x-app-layout>
@endsection

View File

@@ -33,16 +33,6 @@
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'password-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>

View File

@@ -49,16 +49,6 @@
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'profile-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>

View File

@@ -40,6 +40,8 @@
'subscribeUrl' => route('public.subscribe', ['publicPage' => $page]),
'csrfToken' => csrf_token(),
'genericError' => __('Something went wrong. Please try again.'),
'labelDay' => __('day'),
'labelDays' => __('days'),
]))"
>
@if ($logoUrl !== null)
@@ -73,7 +75,7 @@
>
<div>
<div class="font-mono text-2xl font-semibold tabular-nums text-white sm:text-3xl" x-text="pad(days)"></div>
<div class="mt-1 text-xs uppercase tracking-wide text-white/60">{{ __('days') }}</div>
<div class="mt-1 text-xs uppercase tracking-wide text-white/60" x-text="days === 1 ? labelDay : labelDays"></div>
</div>
<div>
<div class="font-mono text-2xl font-semibold tabular-nums text-white sm:text-3xl" x-text="pad(hours)"></div>
@@ -164,7 +166,7 @@
class="mt-2 w-full rounded-lg bg-white px-4 py-3 text-sm font-semibold text-slate-900 shadow transition hover:bg-white/90 focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-slate-900 disabled:cursor-not-allowed disabled:opacity-60"
:disabled="submitting"
>
<span x-show="!submitting">{{ __('Register') }}</span>
<span x-show="!submitting">{{ __('public.register_button') }}</span>
<span x-show="submitting" x-cloak>{{ __('Sending…') }}</span>
</button>
</form>