this repo has no description
at main 5.1 kB view raw
1<script lang="ts"> 2 import { navigate, routes, getFullUrl } from '../lib/router.svelte' 3 import { api, ApiError } from '../lib/api' 4 import { getAuthState } from '../lib/auth.svelte' 5 import { _ } from '../lib/i18n' 6 import type { Session } from '../lib/types/api' 7 import { unsafeAsEmail } from '../lib/types/branded' 8 9 const auth = $derived(getAuthState()) 10 11 function getSession(): Session | null { 12 return auth.kind === 'authenticated' ? auth.session : null 13 } 14 15 const session = $derived(getSession()) 16 17 let email = $state('') 18 let token = $state('') 19 let newPassword = $state('') 20 let confirmPassword = $state('') 21 let submitting = $state(false) 22 let error = $state<string | null>(null) 23 let success = $state<string | null>(null) 24 let tokenSent = $state(false) 25 26 $effect(() => { 27 if (session) { 28 navigate(routes.dashboard) 29 } 30 }) 31 32 async function handleRequestReset(e: Event) { 33 e.preventDefault() 34 if (!email) return 35 submitting = true 36 error = null 37 success = null 38 try { 39 await api.requestPasswordReset(unsafeAsEmail(email)) 40 tokenSent = true 41 success = $_('resetPassword.codeSent') 42 } catch (e) { 43 error = e instanceof ApiError ? e.message : 'Failed to send reset code' 44 } finally { 45 submitting = false 46 } 47 } 48 49 async function handleReset(e: Event) { 50 e.preventDefault() 51 if (!token || !newPassword || !confirmPassword) return 52 if (newPassword !== confirmPassword) { 53 error = $_('resetPassword.passwordsMismatch') 54 return 55 } 56 if (newPassword.length < 8) { 57 error = $_('resetPassword.passwordLength') 58 return 59 } 60 submitting = true 61 error = null 62 success = null 63 try { 64 await api.resetPassword(token, newPassword) 65 success = $_('resetPassword.success') 66 setTimeout(() => navigate(routes.login), 2000) 67 } catch (e) { 68 error = e instanceof ApiError ? e.message : 'Failed to reset password' 69 } finally { 70 submitting = false 71 } 72 } 73</script> 74 75<div class="reset-page"> 76 {#if error} 77 <div class="message error">{error}</div> 78 {/if} 79 {#if success} 80 <div class="message success">{success}</div> 81 {/if} 82 83 {#if tokenSent} 84 <h1>{$_('resetPassword.title')}</h1> 85 <p class="subtitle">{$_('resetPassword.subtitle')}</p> 86 87 <form onsubmit={handleReset}> 88 <div class="field"> 89 <label for="token">{$_('resetPassword.code')}</label> 90 <input 91 id="token" 92 type="text" 93 bind:value={token} 94 placeholder={$_('resetPassword.codePlaceholder')} 95 disabled={submitting} 96 required 97 /> 98 </div> 99 <div class="field"> 100 <label for="new-password">{$_('resetPassword.newPassword')}</label> 101 <input 102 id="new-password" 103 type="password" 104 bind:value={newPassword} 105 placeholder={$_('resetPassword.newPasswordPlaceholder')} 106 disabled={submitting} 107 required 108 minlength="8" 109 /> 110 </div> 111 <div class="field"> 112 <label for="confirm-password">{$_('resetPassword.confirmPassword')}</label> 113 <input 114 id="confirm-password" 115 type="password" 116 bind:value={confirmPassword} 117 placeholder={$_('resetPassword.confirmPasswordPlaceholder')} 118 disabled={submitting} 119 required 120 /> 121 </div> 122 <button type="submit" disabled={submitting || !token || !newPassword || !confirmPassword}> 123 {submitting ? $_('resetPassword.resetting') : $_('resetPassword.resetButton')} 124 </button> 125 <button type="button" class="secondary" onclick={() => { tokenSent = false; token = ''; newPassword = ''; confirmPassword = '' }}> 126 {$_('resetPassword.requestNewCode')} 127 </button> 128 </form> 129 {:else} 130 <h1>{$_('resetPassword.forgotTitle')}</h1> 131 <p class="subtitle">{$_('resetPassword.forgotSubtitle')}</p> 132 133 <form onsubmit={handleRequestReset}> 134 <div class="field"> 135 <label for="email">{$_('resetPassword.handleOrEmail')}</label> 136 <input 137 id="email" 138 type="text" 139 bind:value={email} 140 placeholder={$_('resetPassword.emailPlaceholder')} 141 disabled={submitting} 142 required 143 /> 144 </div> 145 <button type="submit" disabled={submitting || !email}> 146 {submitting ? $_('resetPassword.sending') : $_('resetPassword.sendCode')} 147 </button> 148 </form> 149 {/if} 150 151 <p class="link-text"> 152 <a href="/app/login">{$_('common.backToLogin')}</a> 153 </p> 154</div> 155 156<style> 157 .reset-page { 158 max-width: var(--width-sm); 159 margin: var(--space-9) auto; 160 padding: var(--space-7); 161 } 162 163 h1 { 164 margin: 0 0 var(--space-3) 0; 165 } 166 167 .subtitle { 168 color: var(--text-secondary); 169 margin: 0 0 var(--space-7) 0; 170 } 171 172 form { 173 display: flex; 174 flex-direction: column; 175 gap: var(--space-4); 176 } 177 178 .link-text { 179 text-align: center; 180 margin-top: var(--space-6); 181 color: var(--text-secondary); 182 } 183 184 .link-text a { 185 color: var(--accent); 186 } 187</style>