feat: implement basic auth workflow

This commit is contained in:
2026-04-11 21:36:50 -04:00
parent 652ac1e2af
commit 355f3c5dfa
4 changed files with 158 additions and 18 deletions

View File

@@ -2,17 +2,114 @@
<IonPage>
<IonHeader>
<IonToolbar color="primary">
<IonButtons slot="start">
<IonBackButton default-href="/" />
</IonButtons>
<IonTitle>Sign In</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent class="ion-padding">
<!-- TODO: Auth form -->
<div class="login-form">
<template v-if="!sent">
<p class="ion-text-center">Enter your email address and we'll send you a sign-in link.</p>
<IonList>
<IonItem>
<IonLabel position="stacked">Email address</IonLabel>
<IonInput
v-model="email"
type="email"
placeholder="you@example.com"
autocomplete="email"
inputmode="email"
@keyup.enter="send"
/>
</IonItem>
</IonList>
<IonButton
expand="block"
class="ion-margin-top"
:disabled="!email || loading"
@click="send"
>
<IonSpinner v-if="loading" name="crescent" slot="start" />
Send Sign-In Link
</IonButton>
<p v-if="error" class="error-text ion-text-center">{{ error }}</p>
</template>
<template v-else>
<div class="sent-state ion-text-center">
<IonIcon :icon="mailOutline" class="sent-icon" />
<h2>Check your email</h2>
<p>A sign-in link was sent to <strong>{{ email }}</strong>. Open it on this device to sign in.</p>
<IonButton fill="outline" expand="block" class="ion-margin-top" @click="reset">
Use a different email
</IonButton>
</div>
</template>
</div>
</IonContent>
</IonPage>
</template>
<script setup lang="ts">
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue'
import {
IonPage, IonHeader, IonToolbar, IonTitle, IonContent,
IonButtons, IonBackButton, IonButton, IonList, IonItem,
IonLabel, IonInput, IonSpinner, IonIcon,
} from '@ionic/vue'
import { mailOutline } from 'ionicons/icons'
import { useAuthStore } from '~/stores/auth'
definePageMeta({ layout: false })
const auth = useAuthStore()
const email = ref('')
const loading = ref(false)
const sent = ref(false)
const error = ref('')
async function send() {
if (!email.value || loading.value) return
loading.value = true
error.value = ''
try {
await auth.sendMagicLink(email.value.trim())
sent.value = true
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to send link. Please try again.'
} finally {
loading.value = false
}
}
function reset() {
sent.value = false
email.value = ''
error.value = ''
}
</script>
<style scoped>
.login-form {
max-width: 400px;
margin: 2rem auto;
}
.sent-state {
margin-top: 4rem;
}
.sent-icon {
font-size: 4rem;
color: var(--ion-color-primary);
margin-bottom: 1rem;
}
.error-text {
color: var(--ion-color-danger);
font-size: 0.875rem;
margin-top: 0.5rem;
}
</style>