<template>
|
|
<div class="w-full sm:max-w-sm sm:mx-auto sm:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md">
|
|
<div class="text-center font-thin text-4xl py-4">AUTOPLEX</div>
|
|
<div class="font-medium text-center text-xl">Sign Up</div>
|
|
<form @submit.prevent="register">
|
|
<div class="space-y-4">
|
|
<div>
|
|
<text-box label="Access Token" type="text" disabled
|
|
v-model="fields.token" :validator="validateToken" ref="token"/>
|
|
</div>
|
|
<div>
|
|
<text-box label="Your Name" type="text" placeholder="John Doe" :disabled="isSubmitting"
|
|
v-model="fields.name" :validator="constraints.name" ref="name"/>
|
|
</div>
|
|
<div>
|
|
<text-box label="Email" type="email" placeholder="john@example.com" :disabled="isSubmitting"
|
|
v-model="fields.email" :validator="validateEmail" ref="email"/>
|
|
</div>
|
|
<div>
|
|
<text-box label="Password" type="password" placeholder="············" :disabled="isSubmitting"
|
|
v-model="fields.password" :validator="constraints.password" ref="password"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<text-box label="Re-type Password" type="password" placeholder="············" :disabled="isSubmitting"
|
|
v-model="fields.retypePassword" :validator="validateRetypePassword" ref="retypePassword"/>
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="block w-full rounded-full bg-indigo-500 text-white p-3 focus:outline-none" :disabled="isSubmitting">Register</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, reactive } from "vue";
|
|
import CheckBox from "../components/CheckBox.vue";
|
|
import TextBox from "../components/TextBox.vue";
|
|
import { constraints } from "../../common/validation";
|
|
import { validateValue } from "../util";
|
|
|
|
export default defineComponent({
|
|
components: {
|
|
CheckBox,
|
|
TextBox
|
|
},
|
|
data() {
|
|
return {
|
|
isSubmitting : false,
|
|
fields: reactive({
|
|
token : <string>this.$route.query["token"],
|
|
name : "",
|
|
email : "",
|
|
password : "",
|
|
retypePassword: "",
|
|
}),
|
|
constraints: {
|
|
name: constraints.register.name,
|
|
email: constraints.register.email,
|
|
password: constraints.register.password
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
/**
|
|
* Submit the registration form
|
|
*/
|
|
async register() {
|
|
if (this.isSubmitting) {
|
|
return;
|
|
}
|
|
this.isSubmitting = true;
|
|
fetch(`/auth/register`, {
|
|
method: "post",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify(this.fields)
|
|
})
|
|
.then(async response => {
|
|
this.isSubmitting = false;
|
|
if (response.status !== 200) {
|
|
let body = await response.json();
|
|
if (body.errors) {
|
|
for (let fieldName in this.fields) {
|
|
let field = <any>this.$refs[fieldName];
|
|
let message = <string>(body.errors[fieldName] ?? [""])[0];
|
|
field.setErrorMessage(message);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
this.$router.push({ name: "Login" });
|
|
})
|
|
.catch(e => {
|
|
console.error("Error occurred during submission:", e);
|
|
this.isSubmitting = false;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Validate the provided registration token
|
|
*/
|
|
async validateToken(token: string) {
|
|
let response = await fetch(`/auth/register/validate_token/${token}`, { method: "post" });
|
|
if (response.status !== 200) {
|
|
return "A valid token is required to register";
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Validate the provided email address field
|
|
*/
|
|
async validateEmail(email: string) {
|
|
let error = validateValue(email, constraints.register.email);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
// Check email availability
|
|
let response = await fetch(`/auth/register/available_email/${email}`, { method: "post" });
|
|
if (response.status !== 200) {
|
|
return "An account with that email address already exists";
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Validate the provided retype-password field
|
|
*/
|
|
validateRetypePassword(value: string) {
|
|
if (value.trim().length == 0) {
|
|
return constraints.register.retypePassword.presence.message;
|
|
}
|
|
if (value !== this.fields.password) {
|
|
return constraints.register.retypePassword.equality.message;
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* Navigation Guard
|
|
*/
|
|
beforeRouteEnter(to, from, next) {
|
|
if (to.query["token"] === undefined) {
|
|
next({ name: "Login" });
|
|
} else {
|
|
next();
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style lang="postcss">
|
|
button:focus-visible {
|
|
@apply ring-2 ring-indigo-500 ring-offset-2;
|
|
}
|
|
</style>
|