this repo has no description
1<script lang="ts"> 2 import { navigate } from '../lib/router.svelte' 3 import { api, ApiError } from '../lib/api' 4 import { _ } from '../lib/i18n' 5 6 let identifier = $state('') 7 let submitting = $state(false) 8 let error = $state<string | null>(null) 9 let success = $state(false) 10 11 async function handleSubmit(e: Event) { 12 e.preventDefault() 13 submitting = true 14 error = null 15 16 try { 17 await api.requestPasskeyRecovery(identifier) 18 success = true 19 } catch (err) { 20 if (err instanceof ApiError) { 21 error = err.message || 'Failed to send recovery link' 22 } else if (err instanceof Error) { 23 error = err.message || 'Failed to send recovery link' 24 } else { 25 error = 'Failed to send recovery link' 26 } 27 } finally { 28 submitting = false 29 } 30 } 31</script> 32 33<div class="recovery-page"> 34 {#if success} 35 <div class="success-content"> 36 <h1>{$_('requestPasskeyRecovery.successTitle')}</h1> 37 <p class="subtitle">{$_('requestPasskeyRecovery.successMessage')}</p> 38 <p class="info-text">{$_('requestPasskeyRecovery.successInfo')}</p> 39 <button onclick={() => navigate('/login')}>{$_('requestPasskeyRecovery.backToLogin')}</button> 40 </div> 41 {:else} 42 <h1>{$_('requestPasskeyRecovery.title')}</h1> 43 <p class="subtitle">{$_('requestPasskeyRecovery.subtitle')}</p> 44 45 {#if error} 46 <div class="message error">{error}</div> 47 {/if} 48 49 <form onsubmit={handleSubmit}> 50 <div class="field"> 51 <label for="identifier">{$_('requestPasskeyRecovery.handleOrEmail')}</label> 52 <input 53 id="identifier" 54 type="text" 55 bind:value={identifier} 56 placeholder={$_('requestPasskeyRecovery.emailPlaceholder')} 57 disabled={submitting} 58 required 59 /> 60 </div> 61 62 <div class="info-box"> 63 <strong>{$_('requestPasskeyRecovery.howItWorks')}</strong> 64 <p>{$_('requestPasskeyRecovery.howItWorksDetail')}</p> 65 </div> 66 67 <button type="submit" disabled={submitting || !identifier.trim()}> 68 {submitting ? $_('requestPasskeyRecovery.sending') : $_('requestPasskeyRecovery.sendRecoveryLink')} 69 </button> 70 </form> 71 {/if} 72 73 <p class="link-text"> 74 <a href="#/login">{$_('requestPasskeyRecovery.backToLogin')}</a> 75 </p> 76</div> 77 78<style> 79 .recovery-page { 80 max-width: var(--width-sm); 81 margin: var(--space-9) auto; 82 padding: var(--space-7); 83 } 84 85 h1 { 86 margin: 0 0 var(--space-3) 0; 87 } 88 89 .subtitle { 90 color: var(--text-secondary); 91 margin: 0 0 var(--space-7) 0; 92 } 93 94 form { 95 display: flex; 96 flex-direction: column; 97 gap: var(--space-4); 98 } 99 100 .info-box { 101 background: var(--bg-secondary); 102 border: 1px solid var(--border-color); 103 border-radius: var(--radius-lg); 104 padding: var(--space-5); 105 font-size: var(--text-sm); 106 } 107 108 .info-box strong { 109 display: block; 110 margin-bottom: var(--space-3); 111 } 112 113 .info-box p { 114 margin: 0; 115 color: var(--text-secondary); 116 } 117 118 .success-content { 119 text-align: center; 120 } 121 122 .info-text { 123 color: var(--text-secondary); 124 font-size: var(--text-sm); 125 margin-bottom: var(--space-6); 126 } 127 128 .link-text { 129 text-align: center; 130 margin-top: var(--space-7); 131 } 132 133 .link-text a { 134 color: var(--accent); 135 } 136</style>