|
|
@ -1,26 +1,32 @@ |
|
|
|
<template> |
|
|
|
<div class="w-full max-w-sm lg:mx-auto lg:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md"> |
|
|
|
<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> |
|
|
|
<form @submit.prevent="register"> |
|
|
|
<div class="space-y-4"> |
|
|
|
<div> |
|
|
|
<text-box label="Acess Token" type="text" ref="token" disabled/> |
|
|
|
<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" ref="name" placeholder="John Doe" :error-message="errors.email"/> |
|
|
|
<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" ref="email" placeholder="john@example.com" :error-message="errors.email"/> |
|
|
|
<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" ref="password" placeholder="············" :error-message="errors.password"/> |
|
|
|
<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" ref="password" placeholder="············" :error-message="errors.password"/> |
|
|
|
<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-2 focus:outline-none">Register</button> |
|
|
|
<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> |
|
|
@ -28,39 +34,113 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
|
import { defineComponent } from "vue"; |
|
|
|
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 |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
accessToken(): string { |
|
|
|
return <string>this.$route.query["access_token"]; |
|
|
|
} |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
errors: { |
|
|
|
email: "", |
|
|
|
password: "" |
|
|
|
isSubmitting : false, |
|
|
|
fields: reactive({ |
|
|
|
token : <string>this.$route.query["token"], |
|
|
|
name : "", |
|
|
|
email : "", |
|
|
|
password : "", |
|
|
|
retypePassword: "", |
|
|
|
}), |
|
|
|
constraints: { |
|
|
|
name: constraints.name, |
|
|
|
email: constraints.email, |
|
|
|
password: constraints.password |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
login() { |
|
|
|
/** |
|
|
|
* 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; |
|
|
|
} |
|
|
|
// Register |
|
|
|
}) |
|
|
|
.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.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.retypePassword.presence.message; |
|
|
|
} |
|
|
|
if (value !== this.fields.password) { |
|
|
|
return constraints.retypePassword.equality.message; |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
(<any>this.$refs["token"]).inputValue = this.accessToken; |
|
|
|
console.log("The token is", this.accessToken); |
|
|
|
}, |
|
|
|
/** |
|
|
|
* Navigation Guard |
|
|
|
*/ |
|
|
|
beforeRouteEnter(to, from, next) { |
|
|
|
if (to.query["access_token"] === undefined) { |
|
|
|
if (to.query["token"] === undefined) { |
|
|
|
next({ name: "Login" }); |
|
|
|
} else { |
|
|
|
next(); |
|
|
@ -69,7 +149,7 @@ export default defineComponent({ |
|
|
|
}); |
|
|
|
</script> |
|
|
|
|
|
|
|
<style> |
|
|
|
<style lang="postcss"> |
|
|
|
button:focus-visible { |
|
|
|
@apply ring-2 ring-indigo-500 ring-offset-2; |
|
|
|
} |
|
|
|