fix(auth): toaster on auth pages + audit email-link resets
Codex review follow-ups: extract the notify toaster into a shared partial and include it in the auth layout too, so sendResetLink + recovery-code regenerate actually surface their confirmation (the auth layout had no toaster). Record an AuditEvent for password resets via the email-token path, matching the 2FA path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
3a67aa99ec
commit
2ff2cc34c0
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Models\AuditEvent;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
|
@ -50,6 +51,14 @@ class ResetPassword extends Component
|
|||
'remember_token' => Str::random(60),
|
||||
])->save();
|
||||
|
||||
AuditEvent::create([
|
||||
'user_id' => $user->id,
|
||||
'actor' => $user->name ?? 'system',
|
||||
'action' => 'password.reset',
|
||||
'target' => $user->email,
|
||||
'ip' => request()->ip(),
|
||||
]);
|
||||
|
||||
event(new PasswordReset($user));
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -40,28 +40,7 @@
|
|||
</main>
|
||||
</div>
|
||||
</div>
|
||||
{{-- Toaster: catches Livewire-dispatched `notify` browser events --}}
|
||||
<div
|
||||
x-data="{ toasts: [] }"
|
||||
x-on:notify.window="
|
||||
const msg = $event.detail?.message ?? $event.detail?.[0]?.message ?? @js(__('shell.toast_done'));
|
||||
const id = (window.crypto?.randomUUID?.() ?? String(Date.now() + Math.random()));
|
||||
toasts.push({ id, msg });
|
||||
setTimeout(() => { toasts = toasts.filter(t => t.id !== id) }, 4000);
|
||||
"
|
||||
class="pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col items-center gap-2 px-4 sm:items-end sm:px-6"
|
||||
aria-live="polite"
|
||||
>
|
||||
<template x-for="t in toasts" :key="t.id">
|
||||
<div
|
||||
x-transition.opacity.duration.200ms
|
||||
class="pointer-events-auto flex items-center gap-2.5 rounded-md border border-line bg-raised px-4 py-2.5 shadow-pop"
|
||||
>
|
||||
<span class="h-1.5 w-1.5 shrink-0 rounded-full bg-online"></span>
|
||||
<span class="font-mono text-xs text-ink-2" x-text="t.msg"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@include('partials.toaster')
|
||||
|
||||
{{-- Command palette + keyboard shortcuts (persistent; one global keydown listener) --}}
|
||||
<x-command-palette />
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
</div>
|
||||
</main>
|
||||
</div>
|
||||
@include('partials.toaster')
|
||||
@livewireScripts
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
{{-- Toaster: catches Livewire-dispatched `notify` browser events. Shared by the app
|
||||
and auth layouts so notifications surface on every page. --}}
|
||||
<div
|
||||
x-data="{ toasts: [] }"
|
||||
x-on:notify.window="
|
||||
const msg = $event.detail?.message ?? $event.detail?.[0]?.message ?? @js(__('shell.toast_done'));
|
||||
const id = (window.crypto?.randomUUID?.() ?? String(Date.now() + Math.random()));
|
||||
toasts.push({ id, msg });
|
||||
setTimeout(() => { toasts = toasts.filter(t => t.id !== id) }, 4000);
|
||||
"
|
||||
class="pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col items-center gap-2 px-4 sm:items-end sm:px-6"
|
||||
aria-live="polite"
|
||||
>
|
||||
<template x-for="t in toasts" :key="t.id">
|
||||
<div
|
||||
x-transition.opacity.duration.200ms
|
||||
class="pointer-events-auto flex items-center gap-2.5 rounded-md border border-line bg-raised px-4 py-2.5 shadow-pop"
|
||||
>
|
||||
<span class="h-1.5 w-1.5 shrink-0 rounded-full bg-online"></span>
|
||||
<span class="font-mono text-xs text-ink-2" x-text="t.msg"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
Loading…
Reference in New Issue