Fix tenant event form package selector so it no longer renders empty-value options, handles loading/empty
states, and pulls data from the authenticated /api/v1/tenant/packages endpoint.
(resources/js/admin/pages/EventFormPage.tsx, resources/js/admin/api.ts)
- Harden tenant-admin auth flow: prevent PKCE state loss, scope out StrictMode double-processing, add SPA
routes for /event-admin/login and /event-admin/logout, and tighten token/session clearing semantics (resources/js/admin/auth/{context,tokens}.tsx, resources/js/admin/pages/{AuthCallbackPage,LogoutPage}.tsx,
resources/js/admin/router.tsx, routes/web.php)
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
@viteReactRefresh
|
||||
@vite('resources/js/admin/main.tsx')
|
||||
@vite(['resources/css/app.css', 'resources/js/admin/main.tsx'])
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
@php
|
||||
$scriptNonce = $cspNonce ?? request()->attributes->get('csp_script_nonce');
|
||||
@endphp
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" @class(['dark' => ($appearance ?? 'system') == 'dark'])>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@if($scriptNonce)
|
||||
<meta name="csp-nonce" content="{{ $scriptNonce }}">
|
||||
@endif
|
||||
|
||||
{{-- Inline script to detect system dark mode preference and apply it immediately --}}
|
||||
<script>
|
||||
<script @if($scriptNonce) nonce="{{ $scriptNonce }}" @endif>
|
||||
(function() {
|
||||
const appearance = '{{ $appearance ?? "system" }}';
|
||||
window.__CSP_NONCE = '{{ $scriptNonce }}';
|
||||
|
||||
if (appearance === 'system') {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
@@ -20,17 +28,6 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{-- Inline style to set the HTML background color based on our theme in app.css --}}
|
||||
<style>
|
||||
html {
|
||||
background-color: oklch(1 0 0);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
background-color: oklch(0.145 0 0);
|
||||
}
|
||||
</style>
|
||||
|
||||
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
@@ -41,7 +38,7 @@
|
||||
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />
|
||||
|
||||
@viteReactRefresh
|
||||
@vite(['resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
|
||||
@vite(['resources/css/app.css', 'resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
|
||||
@inertiaHead
|
||||
</head>
|
||||
<body class="font-sans antialiased">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ __('emails.abandoned_checkout.subject_' . $timing, ['package' => $package->name]) }}</title>
|
||||
<title>{{ __('emails.abandoned_checkout.subject_' . $timing, ['package' => $packageName]) }}</title>
|
||||
<style>
|
||||
.cta-button {
|
||||
background-color: #007bff;
|
||||
@@ -26,7 +26,7 @@
|
||||
<body>
|
||||
<h1>{{ __('emails.abandoned_checkout.greeting', ['name' => $user->fullName]) }}</h1>
|
||||
|
||||
<p>{{ __('emails.abandoned_checkout.body_' . $timing, ['package' => $package->name]) }}</p>
|
||||
<p>{{ __('emails.abandoned_checkout.body_' . $timing, ['package' => $packageName]) }}</p>
|
||||
|
||||
<a href="{{ $resumeUrl }}" class="cta-button">
|
||||
{{ __('emails.abandoned_checkout.cta_button') }}
|
||||
@@ -44,4 +44,4 @@
|
||||
|
||||
<p>{!! __('emails.abandoned_checkout.footer') !!}</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
@component('mail::message')
|
||||
# Hallo {{ $name }},
|
||||
# {{ __('emails.contact_confirmation.greeting', ['name' => $name]) }}
|
||||
|
||||
vielen Dank fuer Ihre Nachricht an das Fotospiel Team. Wir melden uns so schnell wie moeglich bei Ihnen.
|
||||
{{ __('emails.contact_confirmation.body') }}
|
||||
|
||||
Falls Sie weitere Informationen hinzufuegen moechten, antworten Sie einfach auf diese E-Mail.
|
||||
|
||||
Viele Gruesse
|
||||
Ihr Fotospiel Team
|
||||
{{ __('emails.contact_confirmation.footer') }}
|
||||
@endcomponent
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ __('emails.purchase.subject', ['package' => $package->name]) }}</title>
|
||||
<title>{{ __('emails.purchase.subject', ['package' => $packageName]) }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ __('emails.purchase.greeting', ['name' => $user->fullName]) }}</h1>
|
||||
<p>{{ __('emails.purchase.package', ['package' => $package->name]) }}</p>
|
||||
<p>{{ __('emails.purchase.package', ['package' => $packageName]) }}</p>
|
||||
<p>{{ __('emails.purchase.price', ['price' => $purchase->price]) }}</p>
|
||||
<p>{{ __('emails.purchase.activation') }}</p>
|
||||
<p>{!! __('emails.purchase.footer') !!}</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -68,6 +68,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$analytics = $token['analytics'] ?? [];
|
||||
@endphp
|
||||
|
||||
@if (!empty($analytics))
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div class="rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-xs text-emerald-800 dark:border-emerald-500/40 dark:bg-emerald-500/10 dark:text-emerald-100">
|
||||
<div class="text-[11px] uppercase tracking-wide">{{ __('admin.events.analytics.success_total') }}</div>
|
||||
<div class="mt-1 text-lg font-semibold">
|
||||
{{ number_format($analytics['success_total'] ?? 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-rose-200 bg-rose-50 p-3 text-xs text-rose-800 dark:border-rose-500/40 dark:bg-rose-500/10 dark:text-rose-100">
|
||||
<div class="text-[11px] uppercase tracking-wide">{{ __('admin.events.analytics.failure_total') }}</div>
|
||||
<div class="mt-1 text-lg font-semibold">
|
||||
{{ number_format($analytics['failure_total'] ?? 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-amber-200 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-100">
|
||||
<div class="text-[11px] uppercase tracking-wide">{{ __('admin.events.analytics.rate_limited_total') }}</div>
|
||||
<div class="mt-1 text-lg font-semibold">
|
||||
{{ number_format($analytics['rate_limited_total'] ?? 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700 dark:border-slate-700 dark:bg-slate-800/70 dark:text-slate-200">
|
||||
<div class="text-[11px] uppercase tracking-wide">{{ __('admin.events.analytics.recent_24h') }}</div>
|
||||
<div class="mt-1 text-lg font-semibold">
|
||||
{{ number_format($analytics['recent_24h'] ?? 0) }}
|
||||
</div>
|
||||
@if (!empty($analytics['last_seen_at']))
|
||||
<div class="mt-1 text-[11px] text-slate-500 dark:text-slate-400">
|
||||
{{ __('admin.events.analytics.last_seen_at', ['date' => \Carbon\Carbon::parse($analytics['last_seen_at'])->isoFormat('LLL')]) }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (!empty($token['layouts']))
|
||||
<div class="mt-4 space-y-3">
|
||||
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@php
|
||||
$scriptNonce = $cspNonce ?? request()->attributes->get('csp_script_nonce');
|
||||
@endphp
|
||||
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@@ -7,7 +11,13 @@
|
||||
<meta name="description" content="Sammle Gastfotos für Events mit QR-Codes und unserer PWA-Plattform. Für Hochzeiten, Firmenevents und mehr. Kostenloser Einstieg.">
|
||||
<link rel="icon" href="{{ asset('logo.svg') }}" type="image/svg+xml">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@if($scriptNonce)
|
||||
<meta name="csp-nonce" content="{{ $scriptNonce }}">
|
||||
@endif
|
||||
@vite(['resources/css/app.css', 'resources/js/app.tsx'])
|
||||
<script @if($scriptNonce) nonce="{{ $scriptNonce }}" @endif>
|
||||
window.__CSP_NONCE = '{{ $scriptNonce }}';
|
||||
</script>
|
||||
|
||||
@php
|
||||
$currentLocale = app()->getLocale();
|
||||
@@ -20,17 +30,6 @@
|
||||
<link rel="alternate" hreflang="{{ $locale }}" href="{{ url("/$locale$path") }}">
|
||||
@endforeach
|
||||
<link rel="alternate" hreflang="x-default" href="{{ url('/de' . $path) }}">
|
||||
<style>
|
||||
@keyframes aurora {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
.bg-aurora {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: aurora 15s ease infinite;
|
||||
}
|
||||
</style>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-gray-50 text-gray-900">
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
<script @if(isset($cspNonce) || request()->attributes->get('csp_script_nonce')) nonce="{{ $cspNonce ?? request()->attributes->get('csp_script_nonce') }}" @endif>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tabLinks = document.querySelectorAll('.tab-link');
|
||||
tabLinks.forEach(link => {
|
||||
@@ -201,4 +201,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endpush
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
@auth
|
||||
@if(auth()->user()->email_verified_at)
|
||||
<script>
|
||||
<script @if(isset($cspNonce) || request()->attributes->get('csp_script_nonce')) nonce="{{ $cspNonce ?? request()->attributes->get('csp_script_nonce') }}" @endif>
|
||||
window.location.href = '/event-admin';
|
||||
</script>
|
||||
<div class="text-center">
|
||||
|
||||
Reference in New Issue
Block a user