@@ -102,7 +102,6 @@ public function backToSetup(): void
102102
103103 public function confirmTwoFactor (ConfirmTwoFactorAuthentication $confirmTwoFactorAuthentication ): void
104104 {
105- dd ($this );
106105 $this -> validate ();
107106 $confirmTwoFactorAuthentication (auth ()-> user (), $this -> authCode );
108107 $this -> twoFactorEnabled = true ;
@@ -152,6 +151,13 @@ public function toggleRecoveryCodes(): void
152151 $this -> showRecoveryCodes = ! $this -> showRecoveryCodes ;
153152 }
154153
154+ public function fetchRecoveryCodes (): void
155+ {
156+ if (! $this -> recoveryCodes ) {
157+ $this -> loadRecoveryCodes ();
158+ }
159+ }
160+
155161 private function loadRecoveryCodes (): void
156162 {
157163 $this -> recoveryCodes = json_decode (decrypt (auth ()-> user ()-> two_factor_recovery_codes ), true );
@@ -162,14 +168,15 @@ private function loadRecoveryCodes(): void
162168 @include (' partials.settings-heading' )
163169 <x-settings .layout :heading =" __('Two Factor Authentication')"
164170 :subheading =" __('Manage your two-factor authentication settings')" >
165- <div class =" flex flex-col w-full mx-auto text-sm space-y-6" >
171+ <div class =" flex flex-col w-full mx-auto text-sm space-y-6" wire:cloak >
166172 @if (! $twoFactorEnabled )
167173 <div class =" relative flex flex-col items-start rounded-xl justify-start space-y-4" >
168174 <flux:badge color =" red" >Disabled</flux:badge >
169- <p class = " text-stone-500 dark:text-stone-400 " >
175+ <flux:text variant = " subtle " >
170176 When you enable two-factor authentication, you will be prompted for a secure pin during login.
171177 This pin can be retrieved from a TOTP-supported application on your phone.
172- </p >
178+ </flux:text >
179+
173180 <div class =" w-auto" >
174181 <flux:button
175182 variant =" primary"
@@ -188,78 +195,93 @@ private function loadRecoveryCodes(): void
188195 <div >
189196 <flux:badge color =" green" >Enabled</flux:badge >
190197 </div >
191- <p class = " text-stone-500 dark :text-stone-400 " >
198+ <flux :text >
192199 With two-factor authentication enabled, you will be prompted for a secure, random pin during
193200 login,
194201 which you can retrieve from the TOTP-supported application on your phone.
195- </p >
196-
197- <div >
198- <flux:callout icon =" lock-keyhole-open" color =" gray" class =" rounded-b-none" >
199- <flux:callout .heading >2FA Recovery Codes</flux:callout .heading >
200- <flux:callout .text >
202+ </flux:text >
203+
204+ <div
205+ class =" flex flex-col gap-6 rounded-xl border border-zinc-200 dark:border-white/10 py-6 shadow-sm"
206+ x-data =" { showRecoveryCodes: {{ $showRecoveryCodes ? ' true' : ' false' } } }" >
207+ <div class =" flex flex-col gap-1.5 px-6" >
208+ <div class =" flex gap-2" >
209+ <flux:icon name =" lock-keyhole" class =" size-4" />
210+ <flux:heading >
211+ 2FA Recovery Codes
212+ </flux:heading >
213+ </div >
214+ <flux:text variant =" subtle" >
201215 Recovery codes let you regain access if you lose your 2FA device. Store them in a
202- secure
203- password manager.
204- </flux:callout .text >
205- </flux:callout >
206- <div
207- class =" bg-stone-100 dark:bg-stone-800 rounded-b-xl border-t-0 border border-stone-200 dark:border-stone-700 text-sm" >
216+ secure password manager.
217+ </flux:text >
218+ </div >
219+ <div class =" px-6" >
220+ <div class =" flex flex-col gap-3 select-none sm:flex-row sm:items-center sm:justify-between" >
221+ <flux:button
222+ x-show =" !showRecoveryCodes"
223+ icon =" eye"
224+ variant =" primary"
225+ @click =" showRecoveryCodes = true"
226+ aria-expanded =" false"
227+ aria-controls =" recovery-codes-section"
228+ >
229+ View Recovery Codes
230+ </flux:button >
231+ <flux:button
232+ x-show =" showRecoveryCodes"
233+ icon =" eye-off"
234+ variant =" primary"
235+ @click =" showRecoveryCodes = false"
236+ aria-expanded =" true"
237+ aria-controls =" recovery-codes-section"
238+ >
239+ Hide Recovery Codes
240+ </flux:button >
241+ <flux:button
242+ x-show =" showRecoveryCodes"
243+ icon =" arrow-path"
244+ variant =" filled"
245+ wire:click =" regenerateRecoveryCodes"
246+ aria-describedby =" regenerate-warning"
247+ >
248+ <span wire:loading.remove
249+ wire:target =" regenerateRecoveryCodes" >{{ __ (' Regenerate Codes' ) } } </span >
250+ <span wire:loading
251+ wire:target =" regenerateRecoveryCodes" >{{ __ (' Regenerating...' ) } } </span >
252+ </flux:button >
253+ </div >
208254 <div
209- wire:click =" toggleRecoveryCodes"
210- class =" h-10 group cursor-pointer flex items-center select-none justify-between px-5 text-xs"
255+ x-show =" showRecoveryCodes"
256+ x-transition
257+ id =" recovery-codes-section"
258+ class =" relative overflow-hidden"
259+ x-bind:aria-hidden =" !showRecoveryCodes"
211260 >
212- <div class =" relative transition-opacity duration-200"
213- @class ([
214- ' opacity-40 group-hover:opacity-60' => ! $showRecoveryCodes ,
215- ' opacity-60' => $showRecoveryCodes
216- ] )>
217- @if (! $showRecoveryCodes )
218- <span class =" flex items-center space-x-1" >
219- <flux:icon .eye class =" size-4" />
220- <span >View My Recovery Codes</span >
221- </span >
222- @else
223- <span class =" flex items-center space-x-1" >
224- <flux:icon .eye-off class =" size-4" />
225- <span >Hide Recovery Codes</span >
226- </span >
227- @endif
228- </div >
229- @if ($showRecoveryCodes )
230- <flux:button
231- size =" xs"
232- variant =" filled"
233- class =" text-stone-600"
234- wire:click.stop =" regenerateRecoveryCodes"
235- wire:loading.attr =" disabled"
236- wire:target =" regenerateRecoveryCodes"
237- >
238- <span wire:loading.remove
239- wire:target =" regenerateRecoveryCodes" >{{ __ (' Regenerate Codes' ) } } </span >
240- <span wire:loading
241- wire:target =" regenerateRecoveryCodes" >{{ __ (' Regenerating...' ) } } </span >
242- </flux:button >
243- @endif
244- </div >
245- @if ($showRecoveryCodes )
246- <div class =" relative" >
261+ <div class =" mt-3 space-y-3" >
247262 <div
248- class =" grid max-w-xl gap-1 px-4 py-4 font-mono text-sm bg-stone-200 dark:bg-stone-900 dark:text-stone-100" >
249- @forelse ($recoveryCodes as $code )
250- <div >{{ $code } } </div >
251- @empty
252- <div class =" text-stone-500" >No recovery codes available</div >
253- @endforelse
263+ class =" grid gap-1 rounded-lg p-4 bg-zinc-200 dark:bg-white/10 font-mono text-sm selection:bg-accent selection:text-accent-foreground"
264+ role =" list" aria-label =" Recovery codes" >
265+
266+ @foreach ($recoveryCodes as $index => $code )
267+ <div role =" listitem"
268+ wire:loading
269+ class =" animate-pulse h-4 opacity-20 rounded bg-zinc-200/80 dark:bg-white/30"
270+ ></div >
271+ <div role =" listitem" wire:loading.class =" hidden"
272+ class =" select-text" >{{ $code } } </div >
273+ @endforeach
274+
254275 </div >
255- <p class = " px-4 py-3 text-xs select-none text-stone-500 dark:text-stone-400 " >
256- You have {{ count ( $recoveryCodes ) } } recovery codes left.
257- Each can be used once to access your account and will be removed after use.
276+ <flux:text variant = " subtle " class = " text-xs" >
277+ Each recovery code can be used once to access your account and will be
278+ removed after use.
258279 If you need more, click <span class =" font-bold" >Regenerate Codes</span >
259280 above.
260- </p >
281+ </flux:text >
282+
261283 </div >
262- @endif
284+ </ div >
263285 </div >
264286 </div >
265287
0 commit comments