implement signup, confirm-email, login, check-email views

This commit is contained in:
Yisroel Baum 2026-05-06 22:49:32 +03:00
parent d85d460fd1
commit ff071c71ca
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
4 changed files with 245 additions and 8 deletions

View file

@ -1,7 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
// CheckEmailPage - filled in by a later branch. // User landing after signup. Tells them to check email.
</script> </script>
<template> <template>
<section><h1>CheckEmailPage</h1></section> <section class="check-email">
<h1>Check your email</h1>
<p>
We sent a confirmation link to your email. Open it to set your password and finish signing up.
</p>
<p class="muted">
<router-link :to="{ name: 'login' }">Back to log in</router-link>
</p>
</section>
</template> </template>
<style scoped>
.check-email {
max-width: 480px;
margin: 32px auto;
}
.muted {
margin-top: 24px;
color: var(--color-secondary);
}
</style>

View file

@ -1,7 +1,74 @@
<script setup lang="ts"> <script setup lang="ts">
// ConfirmEmailPage - filled in by a later branch. import { ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
const auth = useAuthStore();
const route = useRoute();
const router = useRouter();
const password = ref("");
const submitting = ref(false);
async function handleSubmit() {
const token = String(route.query.token ?? "");
if (!token || submitting.value) return;
submitting.value = true;
const ok = await auth.confirmEmail(token, password.value);
submitting.value = false;
if (ok) router.push({ name: "login" });
}
</script> </script>
<template> <template>
<section><h1>ConfirmEmailPage</h1></section> <section class="confirm">
<h1>Set your password</h1>
<form @submit.prevent="handleSubmit">
<label>
<span>Password</span>
<input
v-model="password"
type="password"
required
minlength="8"
autocomplete="new-password"
/>
<small>At least 8 characters.</small>
</label>
<p v-if="auth.error" class="error">{{ auth.error }}</p>
<button type="submit" :disabled="submitting">Confirm</button>
</form>
</section>
</template> </template>
<style scoped>
.confirm {
max-width: 360px;
margin: 32px auto;
}
form {
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 16px;
}
label {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 14px;
}
label small {
color: var(--color-secondary);
font-size: 12px;
}
.error {
color: #b00020;
font-size: 14px;
margin: 0;
}
</style>

View file

@ -1,7 +1,75 @@
<script setup lang="ts"> <script setup lang="ts">
// LoginPage - filled in by a later branch. import { ref } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
const auth = useAuthStore();
const router = useRouter();
const email = ref("");
const password = ref("");
const submitting = ref(false);
async function handleSubmit() {
if (submitting.value) return;
submitting.value = true;
const ok = await auth.login(email.value.trim(), password.value);
submitting.value = false;
if (ok) router.push({ name: "home" });
}
</script> </script>
<template> <template>
<section><h1>LoginPage</h1></section> <section class="login">
<h1>Log in</h1>
<form @submit.prevent="handleSubmit">
<label>
<span>Email</span>
<input v-model="email" type="email" required autocomplete="email" />
</label>
<label>
<span>Password</span>
<input v-model="password" type="password" required autocomplete="current-password" />
</label>
<p v-if="auth.error" class="error">{{ auth.error }}</p>
<button type="submit" :disabled="submitting">Log in</button>
</form>
<p class="muted">
No account?
<router-link :to="{ name: 'signup' }">Sign up</router-link>
</p>
</section>
</template> </template>
<style scoped>
.login {
max-width: 360px;
margin: 32px auto;
}
form {
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 16px;
}
label {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 14px;
}
.error {
color: #b00020;
font-size: 14px;
margin: 0;
}
.muted {
margin-top: 16px;
color: var(--color-secondary);
font-size: 14px;
}
</style>

View file

@ -1,7 +1,89 @@
<script setup lang="ts"> <script setup lang="ts">
// SignupPage - filled in by a later branch. import { ref } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
const auth = useAuthStore();
const router = useRouter();
const email = ref("");
const displayName = ref("");
const submitting = ref(false);
async function handleSubmit() {
if (submitting.value) return;
submitting.value = true;
const ok = await auth.signup(email.value.trim(), displayName.value.trim());
submitting.value = false;
if (ok) router.push({ name: "check-email" });
}
</script> </script>
<template> <template>
<section><h1>SignupPage</h1></section> <section class="signup">
<h1>Sign up</h1>
<form @submit.prevent="handleSubmit">
<label>
<span>Email</span>
<input v-model="email" type="email" required autocomplete="email" />
</label>
<label>
<span>Display name</span>
<input
v-model="displayName"
type="text"
required
minlength="3"
maxlength="30"
pattern="[a-z0-9_-]+"
autocomplete="username"
/>
<small>3-30 chars, lowercase letters, digits, _ or -.</small>
</label>
<p v-if="auth.error" class="error">{{ auth.error }}</p>
<button type="submit" :disabled="submitting">Continue</button>
</form>
<p class="muted">
Already have an account?
<router-link :to="{ name: 'login' }">Log in</router-link>
</p>
</section>
</template> </template>
<style scoped>
.signup {
max-width: 360px;
margin: 32px auto;
}
form {
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 16px;
}
label {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 14px;
}
label small {
color: var(--color-secondary);
font-size: 12px;
}
.error {
color: #b00020;
font-size: 14px;
margin: 0;
}
.muted {
margin-top: 16px;
color: var(--color-secondary);
font-size: 14px;
}
</style>