this repo has no description
1<script lang="ts"> 2 import { login, confirmSignup, resendVerification, getAuthState } from '../lib/auth.svelte' 3 import { navigate } from '../lib/router.svelte' 4 import { ApiError } from '../lib/api' 5 6 let identifier = $state('') 7 let password = $state('') 8 let submitting = $state(false) 9 let error = $state<string | null>(null) 10 11 let pendingVerification = $state<{ did: string } | null>(null) 12 let verificationCode = $state('') 13 let resendingCode = $state(false) 14 let resendMessage = $state<string | null>(null) 15 16 const auth = getAuthState() 17 18 $effect(() => { 19 if (auth.session) { 20 navigate('/dashboard') 21 } 22 }) 23 24 async function handleSubmit(e: Event) { 25 e.preventDefault() 26 if (!identifier || !password) return 27 28 submitting = true 29 error = null 30 pendingVerification = null 31 32 try { 33 await login(identifier, password) 34 navigate('/dashboard') 35 } catch (e: any) { 36 if (e instanceof ApiError && e.error === 'AccountNotVerified') { 37 if (e.did) { 38 pendingVerification = { did: e.did } 39 } else { 40 error = 'Account not verified. Please check your verification method for a code.' 41 } 42 } else { 43 error = e.message || 'Login failed' 44 } 45 } finally { 46 submitting = false 47 } 48 } 49 50 async function handleVerification(e: Event) { 51 e.preventDefault() 52 53 if (!pendingVerification || !verificationCode.trim()) return 54 55 submitting = true 56 error = null 57 58 try { 59 await confirmSignup(pendingVerification.did, verificationCode.trim()) 60 navigate('/dashboard') 61 } catch (e: any) { 62 error = e.message || 'Verification failed' 63 } finally { 64 submitting = false 65 } 66 } 67 68 async function handleResendCode() { 69 if (!pendingVerification || resendingCode) return 70 71 resendingCode = true 72 resendMessage = null 73 error = null 74 75 try { 76 await resendVerification(pendingVerification.did) 77 resendMessage = 'Verification code resent!' 78 } catch (e: any) { 79 error = e.message || 'Failed to resend code' 80 } finally { 81 resendingCode = false 82 } 83 } 84 85 function backToLogin() { 86 pendingVerification = null 87 verificationCode = '' 88 error = null 89 resendMessage = null 90 } 91</script> 92 93<div class="login-container"> 94 {#if error} 95 <div class="error">{error}</div> 96 {/if} 97 98 {#if pendingVerification} 99 <h1>Verify Your Account</h1> 100 <p class="subtitle"> 101 Your account needs verification. Enter the code sent to your verification method. 102 </p> 103 104 {#if resendMessage} 105 <div class="success">{resendMessage}</div> 106 {/if} 107 108 <form onsubmit={(e) => { e.preventDefault(); handleVerification(e); }}> 109 <div class="field"> 110 <label for="verification-code">Verification Code</label> 111 <input 112 id="verification-code" 113 type="text" 114 bind:value={verificationCode} 115 placeholder="Enter 6-digit code" 116 disabled={submitting} 117 required 118 maxlength="6" 119 pattern="[0-9]{6}" 120 autocomplete="one-time-code" 121 /> 122 </div> 123 124 <button type="submit" disabled={submitting || !verificationCode.trim()}> 125 {submitting ? 'Verifying...' : 'Verify Account'} 126 </button> 127 128 <button type="button" class="secondary" onclick={handleResendCode} disabled={resendingCode}> 129 {resendingCode ? 'Resending...' : 'Resend Code'} 130 </button> 131 132 <button type="button" class="tertiary" onclick={backToLogin}> 133 Back to Login 134 </button> 135 </form> 136 {:else} 137 <h1>Sign In</h1> 138 <p class="subtitle">Sign in to manage your PDS account</p> 139 140 <form onsubmit={(e) => { e.preventDefault(); handleSubmit(e); }}> 141 <div class="field"> 142 <label for="identifier">Handle or Email</label> 143 <input 144 id="identifier" 145 type="text" 146 bind:value={identifier} 147 placeholder="you.bsky.social or you@example.com" 148 disabled={submitting} 149 required 150 /> 151 </div> 152 153 <div class="field"> 154 <label for="password">Password</label> 155 <input 156 id="password" 157 type="password" 158 bind:value={password} 159 placeholder="Password" 160 disabled={submitting} 161 required 162 /> 163 </div> 164 165 <button type="submit" disabled={submitting || !identifier || !password}> 166 {submitting ? 'Signing in...' : 'Sign In'} 167 </button> 168 </form> 169 170 <p class="register-link"> 171 Don't have an account? <a href="#/register">Create one</a> 172 </p> 173 {/if} 174</div> 175 176<style> 177 .login-container { 178 max-width: 400px; 179 margin: 4rem auto; 180 padding: 2rem; 181 } 182 183 h1 { 184 margin: 0 0 0.5rem 0; 185 } 186 187 .subtitle { 188 color: var(--text-secondary); 189 margin: 0 0 2rem 0; 190 } 191 192 form { 193 display: flex; 194 flex-direction: column; 195 gap: 1rem; 196 } 197 198 .field { 199 display: flex; 200 flex-direction: column; 201 gap: 0.25rem; 202 } 203 204 label { 205 font-size: 0.875rem; 206 font-weight: 500; 207 } 208 209 input { 210 padding: 0.75rem; 211 border: 1px solid var(--border-color-light); 212 border-radius: 4px; 213 font-size: 1rem; 214 background: var(--bg-input); 215 color: var(--text-primary); 216 } 217 218 input:focus { 219 outline: none; 220 border-color: var(--accent); 221 } 222 223 button { 224 padding: 0.75rem; 225 background: var(--accent); 226 color: white; 227 border: none; 228 border-radius: 4px; 229 font-size: 1rem; 230 cursor: pointer; 231 margin-top: 0.5rem; 232 } 233 234 button:hover:not(:disabled) { 235 background: var(--accent-hover); 236 } 237 238 button:disabled { 239 opacity: 0.6; 240 cursor: not-allowed; 241 } 242 243 button.secondary { 244 background: transparent; 245 color: var(--accent); 246 border: 1px solid var(--accent); 247 } 248 249 button.secondary:hover:not(:disabled) { 250 background: var(--accent); 251 color: white; 252 } 253 254 button.tertiary { 255 background: transparent; 256 color: var(--text-secondary); 257 border: none; 258 } 259 260 button.tertiary:hover:not(:disabled) { 261 color: var(--text-primary); 262 } 263 264 .error { 265 padding: 0.75rem; 266 background: var(--error-bg); 267 border: 1px solid var(--error-border); 268 border-radius: 4px; 269 color: var(--error-text); 270 } 271 272 .success { 273 padding: 0.75rem; 274 background: var(--success-bg); 275 border: 1px solid var(--success-border); 276 border-radius: 4px; 277 color: var(--success-text); 278 } 279 280 .register-link { 281 text-align: center; 282 margin-top: 1.5rem; 283 color: var(--text-secondary); 284 } 285 286 .register-link a { 287 color: var(--accent); 288 } 289</style>